Scale-Out Economics: When a Backend Helps¶
Switching optimize(..., backend=...) from local to a distributed backend
(Spark, Dask, Ray, MPI) is not automatically faster. This page states, from
measurement, when it is expected to help – and when it is not.
How a distributed fit spends its time¶
Each EM iteration on a distributed backend does three things:
Compute – every worker folds its data shard into sufficient statistics. This is O(N / workers) per iteration.
Reduce – each worker’s sufficient-statistic payload, exactly
pickle.dumps((count, accumulator.value())), is reduced to the root (a gather-and-fold or an O(log W) reduce tree, backend-dependent) and combined. Seemixle/utils/parallel/mpi.py.Broadcast – the root re-estimates the model and sends it back to every worker.
The reduce and broadcast are fixed overhead per iteration; only the compute shrinks as workers are added.
The payload is O(model), not O(data)¶
The tested invariant is that the sufficient-statistic payload does not grow
materially with the dataset size. It is a function of the model, not N.
mixle/tests/reduction_payload_telemetry_test.py checks that property for a
single Gaussian and a Gaussian mixture, checks that the larger model has the
larger payload, and verifies serialization round trips. Exact byte counts and
latencies are environment-dependent telemetry; cite them only from a
candidate-bound benchmark receipt.
When distribution is expected to help¶
Because the reduce payload is tiny and fixed while compute scales with N, distribution pays off when per-shard compute time dominates the fixed reduce + fold + broadcast overhead. Concretely:
Helps: large N and non-trivial per-row work – a rich model (large mixtures, HMMs, neural leaves), an expensive encoder, or per-row scoring that is not memory-bandwidth-bound. Here more workers cut the dominant compute term while the byte-sized payload is negligible.
Does not help: small-to-moderate N with a cheap model (for example a plain Gaussian or a small mixture). The per-row work is so cheap that the local fit finishes before a distributed backend has paid its serialization and scheduling overhead. A small reduction payload buys nothing if the whole local fit already finishes quickly.
Backs up the wrong way: a network with high per-message latency penalizes the per-iteration reduce/broadcast on every EM step; iteration count matters, not just data size.
Rule of thumb: distribute when the single-worker fit is compute-bound and takes long enough that cutting compute by the worker count outweighs fixed per-iteration serialization and backend scheduling overhead. If a local fit already finishes quickly, prefer local execution.
Measuring for your own model¶
The payload and its serialization cost for any estimator/model pair can be
measured the same way the telemetry test does: build the accumulator, fold a
shard, and size pickle.dumps((count, accumulator.value())). If that payload
is small and flat in N (it will be for the classical families), the reduce cost
is not your bottleneck, and the decision comes down to per-shard compute versus
backend scheduling latency.