diff --git a/src/python/omnimalloc/analysis/_conflicts.py b/src/python/omnimalloc/analysis/_conflicts.py index 7513183..84c1ac3 100644 --- a/src/python/omnimalloc/analysis/_conflicts.py +++ b/src/python/omnimalloc/analysis/_conflicts.py @@ -34,13 +34,11 @@ def conflict_degrees( ) -> list[int]: """Conflict count per allocation, aligned with input order. - The degree sequence of the conflict relation behind `conflicts`, - without materializing the adjacency. Positional rather than id-keyed, - so duplicate ids are allowed and counted with multiplicity. Scalar - lifetimes count in O(N log N) without enumerating pairs; vector clocks - take the pairwise C++ sweep (quadratic in the worst case), raising - `RuntimeError` once it exceeds `work_budget` — pass `None` to - always compute. + The degree sequence of the relation behind `conflicts`, without + materializing the adjacency; positional rather than id-keyed, so + duplicate ids are allowed and counted with multiplicity. Scalar + lifetimes count in O(N log N); vector clocks take the quadratic sweep, + raising `RuntimeError` past `work_budget` (`None` always computes). """ ensure_valid_budget(work_budget) return _conflict_degrees(allocations, work_budget) diff --git a/src/python/omnimalloc/analysis/_pressure.py b/src/python/omnimalloc/analysis/_pressure.py index 41a3fa8..475385f 100644 --- a/src/python/omnimalloc/analysis/_pressure.py +++ b/src/python/omnimalloc/analysis/_pressure.py @@ -46,9 +46,9 @@ def closure_pressure( C++ enumeration of the join-closure of the birth clocks. Pairwise- concurrent allocations need not share a cut, so this can sit strictly - below `antichain_pressure`; both soundly lower-bound any placement's peak. Raises - `RuntimeError` once the closure exceeds `closure_cap`; pass `None` to - always enumerate — memory then grows with the closure itself. + below `antichain_pressure`; both soundly lower-bound any placement's + peak. Raises `RuntimeError` once the closure exceeds `closure_cap`; + pass `None` to always enumerate — memory then grows with the closure. """ ensure_valid_budget(closure_cap, name="closure_cap") return _closure_pressure(allocations, closure_cap) @@ -57,10 +57,9 @@ def closure_pressure( def placement_pressure(allocations: Sequence[Allocation]) -> int: """Peak of a placement: the highest occupied address, max(offset + size). - Simply the pressure the placement realizes after allocation — an upper - bound on `antichain_pressure` (and so on `closure_pressure`), equal to the max - entry of `placement_pressure_per_allocation`. Raises `ValueError` on - unplaced input. + An upper bound on `antichain_pressure` (and so on `closure_pressure`), + equal to the max entry of `placement_pressure_per_allocation`. Raises + `ValueError` on unplaced input. """ heights = [] for alloc in allocations: @@ -76,15 +75,11 @@ def antichain_pressure_per_allocation( ) -> dict[IdType, int]: """Peak pressure over each allocation's own lifetime, keyed by id. - The max-weight antichain through each allocation: the tightest - order-derived lower bound on the pressure any placement can exhibit - while that allocation is live; the max entry equals `antichain_pressure`. - Linearizable instances take one O(N log N) window sweep, genuinely - partial orders solve one pinned antichain (min flow over the conflict - neighborhood) per distinct lifetime — exact, but built to certify - placements, not for the 10k+ hot path. Raises `RuntimeError` - once the linearize attempt or a pinned flow exceeds `work_budget`; - pass `None` to always compute. + `antichain_pressure` restricted to cuts where each allocation is live; + the max entry equals `antichain_pressure`. Genuinely partial orders + solve one pinned antichain per distinct lifetime. Raises + `RuntimeError` once the linearize attempt or a pinned flow exceeds + `work_budget`; pass `None` to always compute. """ ensure_valid_budget(work_budget) ensure_unique_ids(allocations) @@ -97,12 +92,10 @@ def closure_pressure_per_allocation( ) -> dict[IdType, int]: """Exact realizable peak while each allocation is live, keyed by id. - The max total size at any join-closure cut where the allocation is - live. Can sit elementwise strictly below `antichain_pressure_per_allocation`, - since pairwise-concurrent allocations need not share a cut; the max - entry equals `closure_pressure`. Raises `RuntimeError` once the - closure exceeds `closure_cap`; pass `None` to always enumerate — - memory then grows with the closure itself. + `closure_pressure` restricted to cuts where each allocation is live; + the max entry equals `closure_pressure`, and entries can sit strictly + below `antichain_pressure_per_allocation`. Raises `RuntimeError` once + the closure exceeds `closure_cap`; pass `None` to always enumerate. """ ensure_valid_budget(closure_cap, name="closure_cap") ensure_unique_ids(allocations) diff --git a/src/python/omnimalloc/benchmark/benchmark.py b/src/python/omnimalloc/benchmark/benchmark.py index 00dcf19..b568c9b 100644 --- a/src/python/omnimalloc/benchmark/benchmark.py +++ b/src/python/omnimalloc/benchmark/benchmark.py @@ -154,27 +154,13 @@ def run_benchmark( ) -> BenchmarkCampaign: """Run a benchmark campaign across multiple allocators and sources. - Args: - allocators: Allocators to benchmark (defaults to all available). - sources: Sources to benchmark (defaults to default source). - variants: For parameterizable sources, specifies allocation counts - (int or tuple of ints). For fixed sources, specifies which - models/pools to test (tuple of names or indices, or int for - "first N", or None for all). A dict keyed by source name - selects variants per source. Examples: - - 100: Test with 100 allocations (parameterizable only) - - (10, 100, 1000): Test with multiple sizes (parameterizable) - - ("model1", "model2"): Test specific models (fixed sources) - - 5: Test first 5 models (fixed sources) - - {"random": (10, 100), "minimalloc": 5}: - per-source variants (sources without an entry use defaults) - - None: Use defaults (all models for fixed, 100 for parameterizable) - campaign_id: Unique identifier for this campaign. - iterations: Number of iterations per variant (for statistical averaging). - validate: Whether to validate allocations after running. - - Returns: - BenchmarkCampaign containing all reports. + `allocators` and `sources` default to all available and the default + source. `variants` selects workloads per source: allocation counts + (int or tuple) for parameterizable sources; names, indices, or an int + meaning "first N" for fixed sources; a dict keyed by source name sets + variants per source; `None` uses defaults (all models for fixed, 100 + for parameterizable). `iterations` repeats each variant for + statistical averaging; `validate=True` checks every result. """ allocators = allocators or available_allocators() sources = sources or (DEFAULT_SOURCE,) diff --git a/src/python/omnimalloc/benchmark/results/report.py b/src/python/omnimalloc/benchmark/results/report.py index ec52633..a64b30e 100644 --- a/src/python/omnimalloc/benchmark/results/report.py +++ b/src/python/omnimalloc/benchmark/results/report.py @@ -15,11 +15,7 @@ @dataclass(frozen=True) class BenchmarkReport: - """A collection of benchmark results with aggregate statistics. - - A report aggregates multiple results (iterations) for the same - allocator/source/variant combination. - """ + """Aggregates results (iterations) of one allocator/source/variant combination.""" id: IdType results: tuple[BenchmarkResult, ...] diff --git a/src/python/omnimalloc/benchmark/sources/base.py b/src/python/omnimalloc/benchmark/sources/base.py index 9f30821..d41b2bd 100644 --- a/src/python/omnimalloc/benchmark/sources/base.py +++ b/src/python/omnimalloc/benchmark/sources/base.py @@ -12,19 +12,10 @@ class BaseSource(Registered): """Base class for benchmark allocation sources with automatic registry. - Registry keys drop the class-role token: RandomSource registers as - "random". - - Sources provide workloads at different abstraction levels. - Subclasses must implement `get_allocations()`. Higher-level methods - (pools, memories, systems) have default implementations that build - on allocations. - - Sources can be either: - - Parameterizable: Can generate arbitrary numbers of allocations - (e.g., RandomSource) - - Fixed: Have predetermined models/pools with fixed allocation counts - (e.g., Huggingface) + Subclasses implement `get_allocations()`; the pool/memory/system + methods build on it. Sources are either parameterizable (generate any + allocation count, e.g. RandomSource) or fixed (predetermined + models/pools with set counts, e.g. Huggingface). """ _strip_suffix: ClassVar[str] = "Source" diff --git a/src/python/omnimalloc/benchmark/sources/huggingface.py b/src/python/omnimalloc/benchmark/sources/huggingface.py index 08e93d2..45dca7e 100644 --- a/src/python/omnimalloc/benchmark/sources/huggingface.py +++ b/src/python/omnimalloc/benchmark/sources/huggingface.py @@ -168,11 +168,7 @@ def _download_onnx_models( class HuggingfaceSource(BaseSource): - """Load allocations from Huggingface ONNX models. - - This is a fixed source with predetermined models from Huggingface. - Each model becomes a variant that can be benchmarked. - """ + """Fixed source of Huggingface ONNX model allocations, one variant per model.""" def __init__( self, diff --git a/src/python/omnimalloc/benchmark/sources/minimalloc.py b/src/python/omnimalloc/benchmark/sources/minimalloc.py index df2dfa2..e8c14db 100644 --- a/src/python/omnimalloc/benchmark/sources/minimalloc.py +++ b/src/python/omnimalloc/benchmark/sources/minimalloc.py @@ -23,11 +23,7 @@ class MinimallocSubset(str, Enum): class MinimallocSource(BaseSource): - """Load allocations from a bundled Minimalloc CSV subset. - - This is a fixed source with predetermined pools from the Minimalloc - benchmarks. Pick a bundled ``subset`` to select which pools to load. - """ + """Fixed source loading pools from a bundled Minimalloc CSV ``subset``.""" def __init__( self, diff --git a/src/python/omnimalloc/common/registry.py b/src/python/omnimalloc/common/registry.py index 9aeb167..08cb597 100644 --- a/src/python/omnimalloc/common/registry.py +++ b/src/python/omnimalloc/common/registry.py @@ -13,16 +13,10 @@ class Registered(ABC): """Mixin for auto-registering and managing subclasses. - Any direct subclass of Registered will maintain its own registry. Any - subclass of that subclass that's not abstract will be registered in the - direct subclass's registry. - - Registry names derive from the class name: the root's `_strip_suffix` - (its class-role suffix, e.g. "Allocator" or "Source") is stripped from - the end of the name (mid-name occurrences stay), then the remainder - converts to snake_case — the token carries zero information when the - registry is the namespace. Roots leave `_strip_suffix` empty to keep - full class names. + Each direct subclass of Registered maintains its own registry; its + non-abstract descendants register automatically. Registry names strip + the root's `_strip_suffix` (e.g. "Allocator") from the end of the + class name and snake_case the remainder; roots leave it empty. """ _registry: ClassVar[dict[str, type[Self]]] diff --git a/src/python/omnimalloc/io.py b/src/python/omnimalloc/io.py index 4d35b41..3788e05 100644 --- a/src/python/omnimalloc/io.py +++ b/src/python/omnimalloc/io.py @@ -61,16 +61,14 @@ def save_allocation( ) -> tuple[Path, ...]: """Save the entity's pools to disk as minimalloc-format CSV files. - Saving a `Pool` (or a raw sequence of Allocations, saved as one pool) - writes exactly `path`; a `Memory` or `System` fans out - to one `_.csv` per pool (the CSV is pool-level minimalloc - interchange). Returns the tuple of paths actually written. Pools with - any placed allocation include an `offset` column (minimalloc's solution - format; unplaced rows leave the cell blank), so `load_allocation` - round-trips placements. Vector-clock lifetimes use an - omnimalloc extension (``:``-joined components, e.g. ``3:0``); such files - round-trip through `load_allocation` but are no longer - minimalloc-readable. + A `Pool` (or raw sequence of Allocations, saved as one pool) writes + exactly `path`; a `Memory` or `System` fans out to one + `_.csv` per pool. Returns the paths written. Pools + with any placed allocation include an `offset` column (minimalloc's + solution format; unplaced rows blank), so placements round-trip + through `load_allocation`. Vector-clock lifetimes use an omnimalloc + extension (``:``-joined components, e.g. ``3:0``) that round-trips + but is no longer minimalloc-readable. """ path_ = Path(path) path_.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/python/omnimalloc/primitives/memory.py b/src/python/omnimalloc/primitives/memory.py index b4fa25d..f6cf9d4 100644 --- a/src/python/omnimalloc/primitives/memory.py +++ b/src/python/omnimalloc/primitives/memory.py @@ -17,11 +17,7 @@ @dataclass(frozen=True) class Memory: - """A physical memory unit containing one or more pools. - - `size` is the declared memory size (an input); the computed extent of a - placement is `used_size`. `size=None` means unbounded. - """ + """A physical memory unit containing one or more pools.""" id: IdType pools: tuple[Pool, ...] diff --git a/src/python/omnimalloc/visualize.py b/src/python/omnimalloc/visualize.py index 9d88540..5a1d4ed 100644 --- a/src/python/omnimalloc/visualize.py +++ b/src/python/omnimalloc/visualize.py @@ -558,25 +558,16 @@ def plot_allocation( ) -> None: """Plot an allocated entity: `path=None` displays the figure, `path=...` saves it. - Args: - entity: The entity to plot (System, Memory, Pool, or a raw sequence - of Allocations, plotted as a single pool). - path: Where to save the figure; `None` displays it instead. - capacities: Extra capacity lines to draw, keyed by label then by - memory id (byte limits, drawn as horizontal lines). - view: "panel" draws each memory once over a happens-before-monotone - virtual time (exact for scalar or linearizable lifetimes, else - a sound clock-component-sum projection annotated with its - conflict coverage on modestly sized memories) - "lanes" draws one subplot per thread showing its local-time - projection. Both views only ever show genuine conflicts as - temporal overlaps; scalar entities render identically under - either. - max_lanes: Lanes view only (rejected under other views): cap each - memory to its top-k threads by peak definitely-live - occupancy. - - Raises `ImportError` without matplotlib. + Accepts a System, Memory, Pool, or raw sequence of Allocations + (plotted as a single pool). `capacities` draws extra horizontal limit + lines, keyed by label then memory id. `view="panel"` draws each + memory once over a happens-before-monotone virtual time (exact for + scalar or linearizable lifetimes, else a sound projection annotated + with its conflict coverage); `view="lanes"` draws one subplot per + thread's local-time projection, capped to the top `max_lanes` threads + by peak definitely-live occupancy. Both views only ever show genuine + conflicts as temporal overlaps. Raises `ImportError` without + matplotlib. """ if view not in ("panel", "lanes"): raise ValueError(f'view must be "panel" or "lanes", got {view!r}')