Implementation History

June 8, 2026 · View on GitHub

The in-repo design reference for implementation is design.md.

This file preserves the historical phase roadmap that led to the current implementation on main. It is not the authoritative statement of what ships today. For the current user-facing contract, use README.md, docs/semantics.md, and the implementation in src/.

Phase 1: Types + Clock

Foundation types and injectable clock — everything else depends on these. No tests yet (pFUnit tests come in Phase 2 alongside the core), but these modules must compile cleanly in both serial and MPI builds.

  • src/ftimer_types.F90wp kind parameter, FTIMER_NAME_LEN, error code constants (FTIMER_ERR_NOT_INIT, FTIMER_ERR_UNKNOWN, FTIMER_ERR_ACTIVE, FTIMER_ERR_MISMATCH, FTIMER_ERR_MPI_INCON, FTIMER_ERR_IO), mismatch mode constants (FTIMER_MISMATCH_STRICT, FTIMER_MISMATCH_WARN, FTIMER_MISMATCH_REPAIR), event constants (FTIMER_EVENT_START, FTIMER_EVENT_STOP), ftimer_metadata_t type, ftimer_summary_entry_t type (with name/depth, per-summary node_id/parent_id, and MPI fields defaulting to -1.0_wp), ftimer_summary_t type, ftimer_call_stack_t type (with push, pop, top, equals, copy procedures), ftimer_context_list_t type (with find, add procedures), ftimer_segment_t type, ftimer_clock_func abstract interface, ftimer_hook_proc abstract interface (depends on iso_c_binding)
  • src/ftimer_clock.F90ftimer_default_clock() function using system_clock (double precision), ftimer_mpi_clock() function using MPI_Wtime() (guarded by #ifdef FTIMER_USE_MPI), ftimer_date_string() utility returning formatted date/time string
  • Verify: cmake -B build && cmake --build build compiles both modules (serial). cmake -B build-mpi -DFTIMER_USE_MPI=ON && cmake --build build-mpi compiles with MPI.

Phase 2: Core Timer Class

The ftimer_t class with all timer operations. Write pFUnit tests FIRST for each behavior, then implement.

  • src/ftimer_core.F90ftimer_t derived type with private components: call_stack, segments(:), num_segments, init_wtime, init_date, initialized, mismatch_mode, MPI fields (guarded), clock procedure pointer, on_event procedure pointer, user_data c_ptr
  • ftimer_t%init(...) — Initialize timer. Accept optional keyword mismatch_mode and ierr; in MPI builds, accept optional keyword comm as an mpi_f08 type(MPI_Comm) handle. Set clock to ftimer_default_clock (or ftimer_mpi_clock when MPI), record init_wtime and init_date.
  • ftimer_t%finalize(...) — Deallocate all. Warn/error if timers active. Force-stop all active timers when ierr absent.
  • ftimer_t%start(name, ...) — Lookup/create segment, find/create context for current call stack, push onto stack, record start_time, increment call_count, fire on_event if associated.
  • ftimer_t%stop(name, ...) — Lookup segment (don't create), verify top-of-stack match, pop stack, find context, accumulate time, fire on_event. On mismatch: dispatch to strict/warn/repair.
  • ftimer_t%repair_mismatch(idx) — Capture single now. Unwind stack to target. Accumulate times for unwound timers. Stop target. Restart unwound in reverse. Do NOT fire on_event.
  • ftimer_t%start_id(id, ...) / ftimer_t%stop_id(id, ...) — Fast-path by cached integer ID.
  • ftimer_t%lookup(name, ...) -> id — Get or create integer ID for a timer name.
  • ftimer_t%reset(...) — Zero times/counts, keep definitions. Error if timers active.
  • Private helper: ftimer_t%wtime() — Call self%clock if associated, else ftimer_default_clock().
  • Private helper: ftimer_t%find_or_create_segment(name, status) -> idx — Mapped lookup, grow array if new, report timer-id exhaustion through status.
  • Tests (write BEFORE implementation):
    • tests/test_basic.pf — init/finalize, single start/stop, auto-creation, ID lookup, time accumulation with mock clock
    • tests/test_nesting.pf — 2-level nesting, deep nesting (10+), mismatch in all three modes
    • tests/test_context.pf — Same timer under different parents tracked separately
    • tests/test_callcount.pf — Single call, multiple calls, counts per context
    • tests/test_reset.pf — Reset zeros times/counts, preserves names; reset with active timers
    • tests/test_edge_cases.pf — Stop unknown, start before init, finalize with active, name length limits, ierr contract

Phase 3: Summary Building

Structured summary data + text formatting. Depends on Phase 2.

  • src/ftimer_summary.F90build_summary() subroutine: recursive tree walk building ftimer_summary_t from ftimer_t state. Compute inclusive time per (timer, context). Second pass: compute self_time = inclusive - sum(direct children).
  • Text formatting: format_summary() producing hierarchical indented table with columns: timer name, inclusive time, self time, call count, % of total. Metadata header lines from ftimer_metadata_t array.
  • ftimer_t%get_summary(summary, ...) — Call build_summary(), return structured data.
  • ftimer_t%print_summary(...) — Call get_summary() + format_summary(), write to stdout or specified unit. Accept optional metadata array.
  • ftimer_t%write_summary(...) — Write formatted summary to file (new or append mode). Return FTIMER_ERR_IO on failure.
  • Tests:
    • tests/test_summary.pfget_summary() returns correct structured data (entry count, names, depths, parent linkage, inclusive times, call counts, percentages). Golden text output comparison for print_summary(). Metadata appears in header.
    • tests/test_self_time.pf — Parent(10s) containing child(7s) → parent self_time = 3s. Multiple children. Deeply nested.
    • tests/test_file_output.pf — Write to new file, append to existing, invalid path returns FTIMER_ERR_IO.
    • tests/test_callbacks.pfon_event fires with correct args on normal start/stop. Repair does NOT fire callbacks.

Phase 4: Procedural Convenience API

Default global instance + procedural wrappers. Thin layer over Phase 2-3.

  • src/ftimer.F90 — Module-level default timer instance (ftimer_default_instance) with thin procedural wrappers: ftimer_init, ftimer_finalize, ftimer_start, ftimer_stop, ftimer_start_id, ftimer_stop_id, ftimer_lookup, ftimer_reset, ftimer_get_summary, ftimer_print_summary, ftimer_write_summary.
  • Verify: both use ftimer (procedural) and use ftimer_core (OOP only, no global state) work independently.
  • Update tests to verify procedural interface produces same results as OOP interface.

Phase 5: MPI Support

Cross-rank summary with hash preflight. Depends on Phases 2-3.

  • src/ftimer_mpi.F90 — Hash preflight: each rank hashes sorted canonical timer descriptor list, MPI_Allgather to compare. If mismatch, return FTIMER_ERR_MPI_INCON without producing a global MPI result.
  • MPI reduction now uses MPI_Allreduce to build a distinct ftimer_mpi_summary_t with globally meaningful totals and per-entry min/avg/max data on every participating rank.
  • ftimer_t%mpi_summary(...) — Call hash preflight, then build the global MPI summary object. Successful calls return the same global result on every rank.
  • ftimer_t%print_mpi_summary(...) / write_mpi_summary(...) — First-class communicator-level MPI report output from rank 0.
  • ftimer_mpi_summary procedural wrapper in ftimer.F90.
  • ftimer_print_mpi_summary / ftimer_write_mpi_summary procedural wrappers in ftimer.F90.
  • Tests (tests/mpi/):
    • test_mpi_summary.pf — global MPI summary correctness, canonical ordering, and MPI report output with mock clocks per rank
    • test_mpi_consistency.pf — inconsistent descriptors detected, returns FTIMER_ERR_MPI_INCON with no global MPI result

Phase 6: OpenMP Guards

Master-thread-only timing. Light touch — guards only, not thread-local instances.

  • Add !$omp master / !$omp end master guards around all timer operations in ftimer_core.F90
  • Document OpenMP limitations in docs/semantics.md: master-thread-only, not thread-safe, non-master calls are no-ops
  • Defer any suppress_in_parallel control beyond the current release; Phase 6 keeps the documented master-thread-only no-op semantics

Phase 7: Documentation + Examples

  • docs/semantics.md — Current semantics reference: inclusive/exclusive time definitions, nesting rules, mismatch modes and their behavior, reset behavior, error contract (ierr vs stderr), MPI guarantees, OpenMP limitations, callback contract
  • examples/basic_usage.F90 — Simple start/stop/get_summary/print_summary example
  • examples/nested_timers.F90 — Multi-level nesting example with metadata header fields
  • examples/mpi_example.F90 — MPI summary example showing the current distinct global MPI result and first-class MPI reporting path
  • README.md — Current public contract, quick start, build instructions, and example descriptions

Phase 8: Polish + CI Verification

  • Run fprettify on src/, tests/, and examples/; fix any formatting issues
  • Verify default smoke CI path: cmake -B build-smoke && cmake --build build-smoke && ctest --test-dir build-smoke
  • Verify MPI smoke path on the documented supported toolchain: FC=mpifort cmake -B build-mpi -DFTIMER_USE_MPI=ON && cmake --build build-mpi && ctest --test-dir build-mpi --output-on-failure
  • Verify OpenMP smoke path on the documented supported toolchain: FC=gfortran cmake --fresh -B build-openmp-smoke -DFTIMER_USE_OPENMP=ON && cmake --build build-openmp-smoke && ctest --test-dir build-openmp-smoke --output-on-failure
  • Verify example executables run and match the documented current contract: basic_usage, nested_timers, mpi_example (MPI build), openmp_example (OpenMP build)
  • Final implementation-doc review: CLAUDE.md, AGENTS.md, README.md, and docs/semantics.md all match the code under src/

Verification

  • Default smoke/build-contract baseline passes (ctest --test-dir build-smoke --output-on-failure)
  • Serial pFUnit suite passes (ctest --test-dir build-serial-tests --output-on-failure)
  • MPI pFUnit suite passes (ctest --test-dir build-mpi-tests --output-on-failure -L mpi)
  • OpenMP pFUnit suite passes (ctest --test-dir build-openmp-tests --output-on-failure)
  • Linter clean across src/, tests/, and examples/
  • CI green on all jobs (serial smoke, MPI smoke, OpenMP smoke, build-contract regressions, serial/MPI/OpenMP pFUnit, bench, lint)
  • Implementation documentation accurate and complete (CLAUDE.md, AGENTS.md, README.md, docs/semantics.md)