Size a partition to a per-shard **cycle** (guest-STEP) budget by **bin-packing to the cap**, not by balancing into a fixed shard count. `max_cycles` is the ceiling on a single shard's in-circuit Zisk guest steps — the leaf prover's trace size, which is what sets peak prover RAM. Get it from a host-RAM budget with [`cycle_cap_for_ram`] (the measured prover model `peak_RAM_GiB ≈ 50 + 33 × steps_bil
( profile: &BlockProfile, max_cycles: u64, epsilon: f64, )
| 1843 | fmt_duration(est.wall_secs), |
| 1844 | est.parallelism, |
| 1845 | fmt_duration(est.seq_secs), |
| 1846 | ) |
| 1847 | } |
| 1848 | |
| 1849 | /// A partition sized to a per-shard Zisk **cycle** budget (rather than a fixed |
| 1850 | /// shard count). See [`partition_for_cycle_cap`]. |
| 1851 | pub struct BudgetPlan { |
| 1852 | /// Chosen shard count. |
| 1853 | pub num_shards: usize, |
| 1854 | /// Shard assignment per block id (as [`Hypergraph::partition`] returns). |
| 1855 | pub shard_of: Vec<u32>, |
| 1856 | /// The bisection tree for the chosen partition, for tree-aligned aggregation. |
| 1857 | pub tree: AggNode, |
| 1858 | /// Per-shard predicted-STEPS cap: `max_cycles − SHARD_COST_FLOOR` (the block |
| 1859 | /// step-costs that may sum into one shard before the floor pushes it over |
| 1860 | /// `max_cycles`). |
| 1861 | pub step_cap: u64, |
| 1862 | /// Heaviest shard's **full** predicted guest STEPS — `SHARD_COST_FLOOR + |
| 1863 | /// Σ block_step_cost + COST_PER_INGRESS_BYTE × cross_ingress_bytes`. The |
| 1864 | /// packer keeps this `≤ max_cycles` (cross-ingress included), so it is the |
| 1865 | /// tightest single number to compare against the cap / RAM budget. |
| 1866 | pub max_shard_steps: u64, |
| 1867 | /// Largest single (atomic) block's [`block_step_cost`] — the hard floor on |
| 1868 | /// `max_shard_steps` that no shard count can beat. |
| 1869 | pub largest_block_steps: u64, |
| 1870 | /// True when the largest atomic block alone exceeds the cap: the budget is |
| 1871 | /// infeasible by sharding (split that mutual block upstream, or raise the cap |
| 1872 | /// / use a bigger box). |
| 1873 | pub infeasible_atomic_floor: bool, |
| 1874 | } |
| 1875 | |
| 1876 | /// Size a partition to a per-shard **cycle** (guest-STEP) budget by **bin-packing |
| 1877 | /// to the cap**, not by balancing into a fixed shard count. |
| 1878 | /// |
| 1879 | /// `max_cycles` is the ceiling on a single shard's in-circuit Zisk guest steps |
| 1880 | /// — the leaf prover's trace size, which is what sets peak prover RAM. Get it |
| 1881 | /// from a host-RAM budget with [`cycle_cap_for_ram`] (the measured prover model |
| 1882 | /// `peak_RAM_GiB ≈ 50 + 33 × steps_billions`). |
| 1883 | /// |
| 1884 | /// **Why packing, not balancing.** A shard's predicted STEPS is |
| 1885 | /// `SHARD_COST_FLOOR + Σ block_step_cost + COST_PER_INGRESS_BYTE × |
| 1886 | /// cross_ingress_bytes` (member reduction + own ingress, plus the foreign |
| 1887 | /// dependencies it re-ingresses). The goal is the *fewest* shards that each stay |
| 1888 | /// under the cap — not uniform shards. Balancing into `N = ⌈total/cap⌉` and |
| 1889 | /// growing `N` whenever imbalance pushed the heaviest shard over the cap (the |
| 1890 | /// old approach) *over-sharded*: it spread work evenly, so every shard sat |
| 1891 | /// partially full and a single un-equalizable block forced extra shards. Packing |
| 1892 | /// instead fills each shard as close to the cap as the dependency structure |
| 1893 | /// allows and only opens a new shard when the next block won't fit — giving |
| 1894 | /// `⌈total/cap⌉` shards plus a small packing remainder, each maximally |
| 1895 | /// performant. Lumpy (a near-full shard beside a partial one) is fine. |
| 1896 | /// |
| 1897 | /// **How dependency overlap is packed.** We first cut a *fine* min-cut partition |
| 1898 | /// ([`PACK_PIECES_PER_CAP`] pieces per cap) purely to obtain a **cut-coherent |
| 1899 | /// order**: a DFS of the bisection tree visits each tightly-coupled subtree |
| 1900 | /// before moving on, so dependency-overlapping blocks are contiguous. Greedy |
| 1901 | /// next-fit along that order therefore packs overlapping blocks into the same |
| 1902 | /// shard — keeping their shared dependencies in-shard so they are not re-paid as |