Memory#

pytme computes memory requirements for template matching operations ahead of time, enabling automatic workload splitting across available hardware resources.

Scheduling occurs in three phases:

  1. Predict peak memory consumption for the operation

  2. Determine optimal box decomposition within hardware constraints

  3. Process independent boxes sequentially or in parallel

Core Functions#

compute_schedule(shape, max_memory, ...[, ...])

Plan a parallelization schedule that fits max_memory and max_workers.

estimate_memory_usage(shape1, shape2, ...[, ...])

Estimate the memory usage of a given template matching run.

compute_schedule() determines the box decomposition based on available memory and operation requirements. estimate_memory_usage() predicts peak memory consumption for a given configuration.

Decomposition Strategies#

The scheduler decomposes the search space into boxes that cover the target volume while minimizing computational overhead. Two modes are available:

  • Uniform: Regular grid decomposition across the entire volume

  • Masked: Uses binary segmentation to place boxes only where needed, minimizing the number of computations

(Source code, png, hires.png, pdf)

../../_images/memory-1.png

Defining Memory Requirements#

Custom operations register their memory footprint by subclassing MatchingMemoryUsage and implementing the required methods.

MatchingMemoryUsage(fast_shape, ft_shape, ...)

Strategy class for estimating memory requirements.

A concrete implementation for methods with uniform array requirements is MemoryProfile.

MemoryProfile(fast_shape, ft_shape, ...)

Memory estimator for methods with uniform array requirements.

Built-in Memory Profiles#

Memory profiles are registered for scoring methods, analyzers, and backends. Some profiles serve multiple method identifiers (e.g., CORRMemoryUsage handles CORR, NCC, CAM, and related variants).

Scoring Methods#

CCMemoryUsage(fast_shape, ft_shape, ...)

tme.matching_scores.cc_setup() memory estimator.

CORRMemoryUsage(fast_shape, ft_shape, ...)

tme.matching_scores.corr_setup() memory estimator.

FLCMemoryUsage(fast_shape, ft_shape, ...)

tme.matching_scores.flc_setup() memory estimator.

MCCMemoryUsage(fast_shape, ft_shape, ...)

tme.matching_scores.mcc_setup() memory estimator.

Analyzers#

Backends#

CupyBackendMemoryUsage(fast_shape, ft_shape, ...)

tme.backends.CupyBackend memory estimator.

NumpyBackendMemoryUsage(fast_shape, ...)

tme.backends.NumpyFFTWBackend memory estimator.

Registering Custom Profiles#

Use the register_memory() decorator to associate memory profiles with custom methods.

register_memory(*names)

Register a MatchingMemoryUsage subclass under one or more names.

The decorator accepts multiple method identifiers, allowing a single profile to serve multiple operations:

from tme.memory import MemoryProfile, register_memory

@register_memory("CustomMethod", "CustomMethodVariant")
class CustomMethodMemoryUsage(MemoryProfile):
    """Custom memory estimator."""

    #: Number of shared real arrays
    base_float = 2
    #: Number of shared complex arrays
    base_complex = 1
    #: Number of real arrays per fork
    fork_float = 1
    #: Number of complex arrays per fork
    fork_complex = 1

The MemoryProfile class simplifies registration for methods with uniform array requirements. For more complex memory patterns, subclass MatchingMemoryUsage directly:

from tme.memory import MatchingMemoryUsage, register_memory

@register_memory("ComplexMethod")
class ComplexMethodMemoryUsage(MatchingMemoryUsage):
    """Memory estimator for methods with non-uniform requirements."""

    def base_usage(self):
        # Custom implementation for base usage

    def per_fork(self):
        # Custom implementation per fork

Once registered, the memory profile is automatically used by estimate_memory_usage() and compute_schedule() for the specified method identifiers.