Skip to content

feat: PhaseTimer instrumentation + Eve as default math backend - #65

Merged
foolnotion merged 7 commits into
mainfrom
feat/phase-timer-eve-default
May 31, 2026
Merged

feat: PhaseTimer instrumentation + Eve as default math backend#65
foolnotion merged 7 commits into
mainfrom
feat/phase-timer-eve-default

Conversation

@foolnotion

@foolnotion foolnotion commented May 31, 2026

Copy link
Copy Markdown
Member

Summary

  • PhaseTimer observer: replaces the ad-hoc sort_time_ / SortTime() on GeneticAlgorithmBase with a general tf::ObserverInterface that accumulates wall-clock time per named task into Timings() (Operon::Map<string, double>). The observer is registered before the taskflow runs; each reportProgress lambda holds a shared_ptr to it and syncs Timings() live so the reporter sees up-to-date data each generation. Overhead is ~4 µs/generation (~0.2%), not worth gating.
  • Eve as default math backend: statistical comparison (Mann-Whitney U, 30 seeds, 4 datasets × 3 symbol sets × 2 binaries) shows Eve is 1.21x–1.65x faster than Eigen (median 1.35x, p<0.0001 everywhere) on the current codebase. The AlignedAllocator<T,32> storage from the mdspan refactor provides the alignment Eve needs for optimal SIMD vector loads. Eigen remains selectable via -DMATH_BACKEND=Eigen.
  • compare_operon.py: adds Python-side wall-clock timing (wall_s), drops the broken elapsed column, and adds --ref-build/--new-build options for comparing across build directories (e.g. different backends).
  • CLAUDE.md: project-level guidance to consult official library docs before source.

Test plan

  • Build with default flags — confirm -- MATH: Eve in CMake output
  • Build with -DMATH_BACKEND=Eigen — confirm fallback works
  • Run operon_nsgp and check sort_ms column is non-zero in output
  • Run tools/compare_operon.py with --only-stats and verify wall_s and sort_ms rows appear

Adds PhaseTimer, a tf::ObserverInterface that accumulates wall-clock time
per named task using Operon::Map<string, double>. Replaces the ad-hoc
sort_time_ / SortTime() members on GeneticAlgorithmBase with a general
Timings() accessor backed by phaseTimes_.

The observer is registered on the executor before the taskflow runs and
captures a shared_ptr in each reportProgress lambda so Timings() reflects
live cumulative data during the run, not just post-run. The final copy
after wait() ensures the last generation is included.

reporter.hpp reads sort_ms from Timings().find("non-dominated sort")
with a safe fallback to 0.0 for GP (which has no sort phase).

Overhead is ~4 µs/generation (~0.2% at typical run lengths), too small
to warrant a compile-time gate.
Eve is 1.21x–1.65x faster than Eigen (median 1.35x, p<0.0001 across all
tested dataset/symbol-set combinations) due to better SIMD utilisation,
particularly for transcendental functions. The AlignedAllocator<T,32>
storage introduced for MDArray provides the alignment Eve needs for
optimal vector loads.

Eigen remains selectable via -DMATH_BACKEND=Eigen.
…op elapsed

- Adds wall_s metric: Python-side perf_counter timing of the full
  subprocess call, injected into the stats dict alongside parsed columns.
  More reliable than the parsed elapsed column which was always n/a due
  to integer-zero formatting at generation 0.
- Removes elapsed from REPORT_METRICS and LOWER_IS_BETTER.
- Adds --ref-build / --new-build options (default: "build") to select
  the build subdirectory, enabling comparison across backends without
  moving binaries (e.g. --ref-build build-eigen --new-build build).
- Adds import time (used by wall_s measurement).
- Extract task name into kSortTaskName (string_view constant) to
  eliminate magic-string coupling between nsga2.cpp and reporter.hpp.
  Both sites now reference the constant; a rename is a compile error.

- Add detail::StringHash (transparent, avalanching) so that PhaseTimer's
  internal totals_ map uses heterogeneous lookup. on_exit now calls
  totals_[tv.name()] with no std::string construction after the first
  insert per unique task name. Timings() converts back to a plain
  Operon::Map<string, double> so ga_base.hpp stays unaware of the hash.

- Document that Reset() is only valid between runs: a mid-run Reset()
  would have the next reportProgress sync overwrite the cleared map.
@foolnotion
foolnotion merged commit 9912ee6 into main May 31, 2026
3 checks passed
@foolnotion
foolnotion deleted the feat/phase-timer-eve-default branch May 31, 2026 11:32
foolnotion added a commit that referenced this pull request Jul 13, 2026
feat: PhaseTimer instrumentation + Eve as default math backend
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant