Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
8 changes: 4 additions & 4 deletions benches/PERFORMANCE_RESULTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,11 @@ insphere query performance independently from full triangulation workflows.

## Implementation Notes

### Performance Advantages of `insphere_lifted`
### Dimension-Dependent InSphere Predicate Performance

1. More efficient matrix formulation using relative coordinates
2. Avoids redundant circumcenter calculations
3. Optimized determinant computation
The tables above are the source of truth for predicate timing. `insphere_lifted`
shows advantages in lower dimensions such as 2D/3D, while `insphere_distance`
often wins in 4D/5D; boundary cases may favor `insphere` because of early exits.

## Benchmark Structure

Expand Down
2 changes: 1 addition & 1 deletion benches/ci_performance_suite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ fn interior_facets_4d(dt: &FlipTriangulation4) -> Vec<FacetHandle> {
let mut facets = Vec::new();
for (cell_key, cell) in dt.cells() {
if let Some(neighbors) = cell.neighbors() {
for (facet_index, neighbor) in neighbors.iter().enumerate() {
for (facet_index, neighbor) in neighbors.enumerate() {
if neighbor.is_some() {
let Ok(facet_index) = u8::try_from(facet_index) else {
continue;
Expand Down
18 changes: 15 additions & 3 deletions docs/dev/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,14 @@ just perf-no-regressions

`just perf-no-regressions` is the fast local PR guard. It runs
`ci_performance_suite` with the shared dev-mode Criterion arguments against a
temporary same-machine baseline generated from the current GitHub `main` ref.
The temporary baseline checkout and artifact directory are removed after the
comparison.
same-machine baseline generated from the current GitHub `main` ref. The guard
reuses a local cache under `baseline-artifacts/perf-no-regressions/` keyed by
the resolved `origin/main` commit and local Rust compiler version, and refreshes
that baseline when `main` or the compiler changes, or when the cached artifact
does not match the benchmark contract. The current worktree benchmark still runs
fresh each time so repeated comparisons can catch local performance drift.
`just clean` removes Criterion data under `target/`, but it does not remove this
local baseline cache.

```bash
just perf-no-regressions
Expand All @@ -213,6 +218,13 @@ just perf-no-regressions
you want to create or refresh `baseline-artifact/baseline_results.txt` for later
manual comparisons.

For lower-level workflows, `uv run benchmark-utils ensure-ref-baseline --ref
<ref> --dev` prints the cached/generated same-machine baseline path for a branch
or version tag, and `uv run benchmark-utils fetch-baseline --ref <ref>` downloads
the GitHub Actions artifact instead. Use the generated local baseline for
same-machine regression checks; use the downloaded artifact when you explicitly
want CI-runner parity.

To generate a scratch baseline without replacing the default artifact, write it
somewhere else and compare directly:

Expand Down
21 changes: 14 additions & 7 deletions docs/dev/tooling-alignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,20 @@ Some causal-triangulations tooling remains project-specific and was not ported:
- CDT's `performance-analysis` script; Delaunay keeps its benchmark helpers and
generated performance-summary workflow.
- Delaunay has a project-specific `just perf-no-regressions` recipe that runs
the calibrated 2D-5D `ci_performance_suite` canaries against the current
dev-mode baseline artifact. This stays local to Delaunay because the
benchmark contract, fixture sizes, and regression threshold are tied to this
library's triangulation performance expectations. `just perf-baseline [ref]`
generates that baseline on the developer's machine from a temporary checkout
of the requested GitHub ref so the comparison uses the same local hardware as
the current branch while GitHub Actions still publishes shared CI artifacts.
the calibrated 2D-5D `ci_performance_suite` canaries against a cached
same-machine dev-mode baseline for the current GitHub `main` commit. This
stays local to Delaunay because the benchmark contract, fixture sizes, and
regression threshold are tied to this library's triangulation performance
expectations. The cache lives under
`baseline-artifacts/perf-no-regressions/`, is keyed by the resolved
`origin/main` commit and local Rust compiler version, and is refreshed when
either key changes or the artifact no longer matches the benchmark contract.
The cache/validation logic lives in `benchmark-utils ensure-ref-baseline` and
`benchmark-utils compare-ref` so workflows can reuse the same behavior without
depending on justfile shell internals.
`just perf-baseline [ref]` remains the manual persistent-baseline workflow for
a requested GitHub ref, so ad-hoc comparisons still use the developer's local
hardware while GitHub Actions publishes shared CI artifacts.
- Delaunay keeps a single `profiling_suite` benchmark target for manual
large-scale and flamegraph work. The previous standalone large-scale target
was folded into that harness so `.github/workflows/profiling-benchmarks.yml`
Expand Down
2 changes: 1 addition & 1 deletion examples/delaunayize_repair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ fn flip_then_repair_2d() -> Result<(), DelaunayizeRepairExampleError> {
let mut facets: Vec<_> = Vec::new();
for (ck, cell) in dt.cells() {
if let Some(neighbors) = cell.neighbors() {
for (i, n) in neighbors.iter().enumerate() {
for (i, n) in neighbors.enumerate() {
if let (Some(_), Ok(idx)) = (n, u8::try_from(i)) {
facets.push(FacetHandle::new(ck, idx));
}
Expand Down
2 changes: 1 addition & 1 deletion examples/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn build_non_delaunay_triangulation_2d()

for (cell_key, cell) in dt.cells() {
if let Some(neighbors) = cell.neighbors() {
for (facet_index, neighbor) in neighbors.iter().enumerate() {
for (facet_index, neighbor) in neighbors.enumerate() {
if neighbor.is_none() {
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/pachner_roundtrip_4d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ fn collect_interior_facets(dt: &Dt4) -> Vec<FacetHandle> {
let mut facets = Vec::new();
for (cell_key, cell) in dt.cells() {
if let Some(neighbors) = cell.neighbors() {
for (facet_index, neighbor) in neighbors.iter().enumerate() {
for (facet_index, neighbor) in neighbors.enumerate() {
if neighbor.is_some() {
let Ok(facet_index) = u8::try_from(facet_index) else {
continue;
Expand Down
4 changes: 2 additions & 2 deletions examples/topology_editing_2d_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ fn find_interior_facet_2d<K: Kernel<2>>(
) -> Option<FacetHandle> {
for (cell_key, cell) in dt.cells() {
if let Some(neighbors) = cell.neighbors() {
for (facet_idx, neighbor) in neighbors.iter().enumerate() {
for (facet_idx, neighbor) in neighbors.enumerate() {
if neighbor.is_some() {
let Ok(facet_idx) = u8::try_from(facet_idx) else {
continue;
Expand All @@ -578,7 +578,7 @@ fn find_interior_facet_3d<K: Kernel<3>>(
) -> Option<FacetHandle> {
for (cell_key, cell) in dt.cells() {
if let Some(neighbors) = cell.neighbors() {
for (facet_idx, neighbor) in neighbors.iter().enumerate() {
for (facet_idx, neighbor) in neighbors.enumerate() {
if neighbor.is_some() {
let Ok(facet_idx) = u8::try_from(facet_idx) else {
continue;
Expand Down
55 changes: 4 additions & 51 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ perf-compare file threshold="7.5": _ensure-uv

perf-help:
@echo "Performance Analysis Commands:"
@echo " just perf-no-regressions # Fast pre-PR guard with a temporary same-machine main baseline"
@echo " just perf-no-regressions # Fast pre-PR guard with a cached same-machine main baseline"
@echo " just perf-baseline [ref] # Persist/update baseline-artifact for a GitHub ref (default: main)"
@echo " just perf-baseline-to <out> [ref] # Generate a scratch baseline artifact without replacing the default"
@echo " just perf-compare <file> [threshold] # Compare current tree with a specific dev-mode baseline"
Expand All @@ -368,7 +368,7 @@ perf-help:
@echo " just profile-mem # Samply profile memory allocations (with count-allocations feature)"
@echo ""
@echo "Benchmark System (Delaunay-specific):"
@echo " just perf-no-regressions # Generate temporary main baseline, compare current tree, clean up"
@echo " just perf-no-regressions # Reuse cached main baseline, compare current tree"
@echo " just perf-baseline [ref] # Persist baseline-artifact/baseline_results.txt from a GitHub ref"
@echo " just perf-baseline-to <out> [ref] # Generate an alternate local baseline artifact directory"
@echo " just perf-compare <file> # Compare against a specific dev-mode baseline"
Expand All @@ -395,56 +395,9 @@ perf-help:
@echo " just profile 1.95 # Current tree on Rust 1.95"
@echo " just profile 1.95 v0.7.5 # v0.7.5 code on Rust 1.95"

# Fast pre-PR performance guard against a temporary same-machine main baseline.
# Fast pre-PR performance guard against a cached same-machine main baseline.
perf-no-regressions threshold="7.5": _ensure-uv
#!/usr/bin/env bash
set -euo pipefail

relevant_worktree_dirty() {
if ! git diff --quiet -- src benches Cargo.toml Cargo.lock scripts/benchmark_utils.py; then
return 0
fi
if ! git diff --cached --quiet -- src benches Cargo.toml Cargo.lock scripts/benchmark_utils.py; then
return 0
fi
if [ -n "$(git ls-files --others --exclude-standard -- src benches Cargo.toml Cargo.lock scripts/benchmark_utils.py)" ]; then
return 0
fi
return 1
}

current_commit="$(git rev-parse HEAD)"
remote_line="$(git ls-remote origin refs/heads/main || true)"
remote_main_commit=""
if [ -n "$remote_line" ]; then
read -r remote_main_commit _ <<< "$remote_line"
fi
if [ -n "$remote_main_commit" ] && [ "$remote_main_commit" = "$current_commit" ] && ! relevant_worktree_dirty; then
echo "🔍 origin/main matches HEAD (${current_commit}); no relevant worktree changes to compare."
echo " Skipping perf-no-regressions before generating a same-commit baseline."
exit 0
fi

tmp="$(mktemp -d "${TMPDIR:-/tmp}/delaunay-perf-baseline.XXXXXX")"
trap 'rm -rf "$tmp"' EXIT
uv run benchmark-utils generate-ref-baseline --ref main --out "$tmp/baseline" --dev
baseline="$tmp/baseline/baseline_results.txt"
if ! grep -q 'Benchmark ID: tds_new_2d/tds_new/2000' "$baseline"; then
echo "❌ Temporary baseline for main does not match the current ci_performance_suite contract."
echo " The benchmark contract probably changed on this branch; inspect ci_performance_suite before comparing."
exit 1
fi
baseline_line="$(grep -m1 '^Git commit:' "$baseline" || true)"
baseline_commit="${baseline_line#Git commit: }"
if [ -n "$baseline_commit" ] && [ "$baseline_commit" = "$current_commit" ]; then
if ! relevant_worktree_dirty; then
echo "🔍 Current commit matches the main baseline (${baseline_commit}); no relevant worktree changes to compare."
echo " Skipping perf-no-regressions because a same-commit baseline would mask regressions."
exit 0
fi
echo "⚠️ Main baseline commit matches HEAD, but relevant uncommitted changes exist; comparing the worktree against HEAD."
fi
uv run benchmark-utils compare --baseline "$baseline" --threshold {{threshold}} --dev
uv run benchmark-utils compare-ref --ref main --threshold {{threshold}} --dev

# Run the selected CI benchmark suite for one compiler/code pair.
profile toolchain="" code_ref="current":
Expand Down
Loading
Loading