mixle.utils.parallel.balance module¶
Automatic compute / memory / load-balancing planner for EM estimation of any mixle model.
The currency is FLOPs per iteration, with memory as a hard constraint – memory decides whether the model fits, compute decides how long the iteration takes, and the planner balances compute across the cluster subject to memory.
An EM iteration is a fixed amount of work W = N * C FLOPs (C = per-observation model cost from
compute_cost()). A worker grid of D data-parallel replicas x M model-shards gives every
worker W / (D*M) FLOPs, so the iteration time is max_worker_FLOPs / throughput + coupling. To
balance the load we therefore:
prefer data parallelism (
D) – it has no cross-worker coupling (a data point’s whole model lives on one worker) and balances trivially by equal row counts;use model parallelism (
M) only as forced – by memory (the model does not fit:M >= ceil(bytes/mem)) or by compute concurrency (too few data points to fill the cluster: withNpoints onlyNdata-replicas exist, so the rest of the cluster can only be used by splitting the model);balance the model split by FLOPs, not bytes – a memory-light but compute-heavy leaf (a GP, a big quadratic form) must not become the straggler everyone waits on.
This covers the whole spectrum the same way: a tiny model on lots of data -> M=1, D=P (data-parallel);
a model too big for one worker -> M from memory, D fills the rest; a huge model on a single
observation (N=1) -> D=1, M= as many model shards as the model exposes; and an unbalanced
heterogeneous nest -> the FLOP cost model finds where the work is and the split equalizes it.
- class BalancePlan(data_parallel, model_parallel, workers_used, workers_total, axis, model_cuts, model_flops, model_bytes, per_worker_flops, fits, rationale, extra=<factory>)[source]
Bases:
objectA worker-grid assignment for one EM estimation, balanced by compute under a memory constraint.
- Parameters:
- data_parallel: int
- model_parallel: int
- workers_used: int
- workers_total: int
- axis: DecompAxis
- model_cuts: tuple[ModelCut, ...]
- model_flops: float
- model_bytes: int
- per_worker_flops: float
- fits: bool
- rationale: str
- property is_model_parallel: bool
- property workers_idle: int
- balance_plan(model, resources, *, n_data)[source]
Choose the
(D data-parallel) x (M model-parallel)worker grid that balances compute under memory.Searches
Mfrom the memory-required minimum up to the model’s splittable units, picking the grid that keeps the most workers busy (ties broken toward smallerM– less coupling). Works for any model: a model with no splittable axis simply getsM=1(data-parallel / single worker).
- auto_balanced_estimator(estimator, model, resources=None, *, n_data)[source]
Realize
balance_plan()– return(estimator, plan)ready to driveoptimize.When the plan is model-parallel the estimator is wrapped in
ModelParallelEstimatorsized toplan.model_parallel(the model axis distributes inside each worker / across the model shards); otherwise the plain estimator is returned (pure data-parallel). The data degreeplan.data_parallelis realized by the data backend you pass tooptimize("local"|"mp"|"spark"|"mpi"), sooptimize(data, returned_estimator, backend=...)runs the fullD x Mgrid.resourcesdefaults to the local CPU slots; passResources.from_spark(sc)/from_mpi()to plan for a real cluster.