Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Currently, Argon supports the following features:
- Live reload of GUI upon changes in code editor
- Parametric cells
- Hierarchy
- General linear constraint solving (slow)
- Linear constraint solving: fast sparse elimination, with a general (dense) solver as fallback
- Basic diagnostic reporting in the code editor
- Basic detection of under/overconstrained systems

Expand Down
47 changes: 28 additions & 19 deletions bench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ how an axis scales — without editing any source. Pass a comma-separated list:
| `ARGON_BENCH_SHAPES` | shapes (recursion) | `500,1000,2000,4000,8000,16000,32000` |
| `ARGON_BENCH_SHAPES_LOOP` | shapes (`for` loop) | `500,1000,2000,4000,8000,16000,32000` |
| `ARGON_BENCH_INSTANCES` | instances | `500,…,64000` |
| `ARGON_BENCH_CONSTRAINTS` | coupled constraints | `32,64,128,256,512,1024` |
| `ARGON_BENCH_CONSTRAINTS` | coupled constraints | `32,64,128,256,512,1024,2048,4096,8192,16384` |
| `ARGON_BENCH_HIER_SINGLE` | hierarchy (1 ref) | `4,8,16,32,48,64,96,128` |
| `ARGON_BENCH_HIER_DOUBLE` | hierarchy (2 refs) | `4,8,16,32,48,64,96,128` |

Expand Down Expand Up @@ -131,7 +131,7 @@ parameter; "peak" is peak heap allocated during compilation.
| Shapes (recursion) | 32 000 rects | 1.52 s | 0.89 GiB | **~linear** (time `∝ n^1.2`, mem `∝ n^1.0`) |
| Instances | 64 000 insts | 3.08 s | 1.26 GiB | **~linear** (time `∝ n^1.2`, mem `∝ n^1.0`) |
| Hierarchy, 1 child ref | depth 128 | 0.005 s | 11 MiB | **linear** in depth |
| Coupled constraints | 1 024 rects | 22.0 s | 0.12 GiB | **super-cubic in time** (see below) |
| Coupled constraints | 16 384 rects | 1.76 s | 0.59 GiB | **~linear** (time `∝ n^1.04`, mem `∝ n^0.90`) |
| Shapes (`for`-loop) | 32 000 rects | 1.06 s | 0.85 GiB | **~linear** (time `∝ n^1.2`, mem `∝ n^1.0`) |
| Hierarchy, 2 child refs | depth 128 | 0.006 s | 11 MiB | **linear** in depth (was exponential before the shared-type fix) |

Expand All @@ -145,16 +145,23 @@ parameter; "peak" is peak heap allocated during compilation.
back-substitution without ever forming a matrix. This is the common case for
real parametric cells and it scales comfortably to "thousands of rectangles".

- **Coupled constraints are the expensive axis.** When constraints form one
large connected component that *cannot* be back-substituted (here, a ring of
mutually-coupled edges), Argon falls back to its general linear solver, which
builds a dense matrix and takes an SVD. The per-doubling cost climbs from ~4×
at `n=64→128` to ~15× at `n=512→1024`, i.e. it steepens toward the `O(n^3)`
of dense factorization (and worse, because `solve()` is re-run as the system
is assembled). This is the "general linear constraint solving (slow)" caveat
in the top-level README, quantified: ~1 000 coupled editable variables take
~22 s. Layouts whose constraints decompose into many small independent groups
(the typical case) avoid this entirely.
- **Coupled constraints now scale linearly too.** When constraints form one
large connected component that 1-variable back-substitution cannot crack
(here, a ring of mutually-coupled edges), the solver first runs a sparse
*elimination pre-pass*: it generalizes back-substitution to 2-variable
"definitional" constraints, expressing one variable in terms of another and
substituting it out of the few constraints that mention it. Because a
2-variable constraint replaces one variable with one variable, this never
increases any constraint's size — the ring's leaf edges drop out and its
chain telescopes to a single closure, so the system is solved in `O(n)` and
the dense SVD never runs. The general dense solver is retained only as a
fallback for an *irreducible* coupled core (a block with no ≤2-variable
pivot). This axis used to be **super-cubic** — `~22 s` at `n=1024`, steepening
toward the `O(n^3)` of dense factorization — and is now `~n^1.04` in time
(`1.76 s` at `n=16384`, 16× larger, in less memory than the old `n=1024`
dense matrix used). The "general linear constraint solving (slow)" caveat in
the top-level README now bites only for genuinely dense coupled blocks, not
for the common sparse-but-coupled case.

- **Hierarchy depth scales linearly.** A cell's static type (`CellTy`) records
the structural type of every field, including instantiated sub-cells. That
Expand Down Expand Up @@ -185,12 +192,14 @@ parameter; "peak" is peak heap allocated during compilation.
constant — `shapes_loop` is even marginally faster, as the native `range`
avoids the per-element recursion overhead of `emit_shapes`.

The takeaways for the paper: editable-object count, instance count, and
hierarchy depth all scale linearly; the one practically-relevant limit is the
dense general constraint solver on large *coupled* systems, which lines up with
the future-work item already listed in the project README (faster linear
constraint solving). The bullets above describe the build at the time of
measurement; because every axis is re-runnable (and size-configurable), the same
harness can be used to confirm improvements from compiler optimizations.
The takeaways for the paper: editable-object count, instance count, hierarchy
depth, and now coupled-constraint count all scale linearly. The dense general
solver is no longer a practical limit for sparse-but-coupled systems — the
elimination pre-pass reduces them first — and remains the fallback only for
irreducible dense constraint blocks with no ≤2-variable pivot, which lines up
with the "faster linear constraint solving" future-work item in the project
README. The bullets above describe the build at the time of measurement; because
every axis is re-runnable (and size-configurable), the same harness can be used
to confirm improvements from compiler optimizations.

![Argon scaling](argon_scaling.png)
Binary file modified bench/argon_scaling.pdf
Binary file not shown.
Binary file modified bench/argon_scaling.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 10 additions & 6 deletions bench/results/constraints.csv
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
size,time_s,peak_bytes,n_objects
32,0.003860995,2558554,33
64,0.00719482,3802170,65
128,0.029517606,6722670,129
256,0.224260882,15369454,257
512,1.435889679,42100206,513
1024,21.963011181,133310430,1025
32,0.003404379,2558933,33
64,0.004213333,3802613,65
128,0.007537572,6289973,129
256,0.014580103,11264693,257
512,0.02941705,21214133,513
1024,0.063108165,41113013,1025
2048,0.137085263,80910773,2049
4096,0.306775942,160506293,4097
8192,0.819698386,319697317,8193
16384,1.762328533,638079381,16385
10 changes: 7 additions & 3 deletions core/compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,9 @@ mod tests {
write_bench_csv("shapes_loop", &rows);
}

/// Axis 2: number of mutually-coupled constraints solved by the general
/// (dense) linear-constraint solver.
/// Axis 2: number of mutually-coupled constraints. The coupled ring is reduced by
/// the solver's sparse elimination pre-pass (2-variable constraints telescope away);
/// the general dense SVD remains as a fallback for any irreducible coupled core.
#[test]
#[ignore = "scaling benchmark; run in release, serially: cargo test -p compiler --release -- --ignored --test-threads=1 bench_"]
fn bench_constraints() {
Expand All @@ -381,7 +382,10 @@ mod tests {
assert!(o.static_errors().is_empty(), "{:?}", o.static_errors());
let ast = o.ast();
let mut rows = Vec::new();
for &n in &bench_sizes("ARGON_BENCH_CONSTRAINTS", &[32, 64, 128, 256, 512, 1024]) {
for &n in &bench_sizes(
"ARGON_BENCH_CONSTRAINTS",
&[32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384],
) {
let (dt, mem, out) = measure(1, || {
compile(
&ast,
Expand Down
Loading
Loading