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.F90—wpkind 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_ttype,ftimer_summary_entry_ttype (withname/depth, per-summarynode_id/parent_id, and MPI fields defaulting to-1.0_wp),ftimer_summary_ttype,ftimer_call_stack_ttype (withpush,pop,top,equals,copyprocedures),ftimer_context_list_ttype (withfind,addprocedures),ftimer_segment_ttype,ftimer_clock_funcabstract interface,ftimer_hook_procabstract interface (depends oniso_c_binding) -
src/ftimer_clock.F90—ftimer_default_clock()function usingsystem_clock(double precision),ftimer_mpi_clock()function usingMPI_Wtime()(guarded by#ifdef FTIMER_USE_MPI),ftimer_date_string()utility returning formatted date/time string - Verify:
cmake -B build && cmake --build buildcompiles both modules (serial).cmake -B build-mpi -DFTIMER_USE_MPI=ON && cmake --build build-mpicompiles 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.F90—ftimer_tderived type with private components:call_stack,segments(:),num_segments,init_wtime,init_date,initialized,mismatch_mode, MPI fields (guarded),clockprocedure pointer,on_eventprocedure pointer,user_datac_ptr -
ftimer_t%init(...)— Initialize timer. Accept optional keywordmismatch_modeandierr; in MPI builds, accept optional keywordcommas anmpi_f08type(MPI_Comm)handle. Set clock toftimer_default_clock(orftimer_mpi_clockwhen MPI), recordinit_wtimeandinit_date. -
ftimer_t%finalize(...)— Deallocate all. Warn/error if timers active. Force-stop all active timers whenierrabsent. -
ftimer_t%start(name, ...)— Lookup/create segment, find/create context for current call stack, push onto stack, record start_time, increment call_count, fireon_eventif associated. -
ftimer_t%stop(name, ...)— Lookup segment (don't create), verify top-of-stack match, pop stack, find context, accumulate time, fireon_event. On mismatch: dispatch to strict/warn/repair. -
ftimer_t%repair_mismatch(idx)— Capture singlenow. Unwind stack to target. Accumulate times for unwound timers. Stop target. Restart unwound in reverse. Do NOT fireon_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()— Callself%clockif associated, elseftimer_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 clocktests/test_nesting.pf— 2-level nesting, deep nesting (10+), mismatch in all three modestests/test_context.pf— Same timer under different parents tracked separatelytests/test_callcount.pf— Single call, multiple calls, counts per contexttests/test_reset.pf— Reset zeros times/counts, preserves names; reset with active timerstests/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.F90—build_summary()subroutine: recursive tree walk buildingftimer_summary_tfromftimer_tstate. 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 fromftimer_metadata_tarray. -
ftimer_t%get_summary(summary, ...)— Callbuild_summary(), return structured data. -
ftimer_t%print_summary(...)— Callget_summary()+format_summary(), write to stdout or specified unit. Accept optionalmetadataarray. -
ftimer_t%write_summary(...)— Write formatted summary to file (new or append mode). ReturnFTIMER_ERR_IOon failure. - Tests:
tests/test_summary.pf—get_summary()returns correct structured data (entry count, names, depths, parent linkage, inclusive times, call counts, percentages). Golden text output comparison forprint_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 returnsFTIMER_ERR_IO.tests/test_callbacks.pf—on_eventfires 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) anduse 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_Allgatherto compare. If mismatch, returnFTIMER_ERR_MPI_INCONwithout producing a global MPI result. - MPI reduction now uses
MPI_Allreduceto build a distinctftimer_mpi_summary_twith 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_summaryprocedural wrapper inftimer.F90. -
ftimer_print_mpi_summary/ftimer_write_mpi_summaryprocedural wrappers inftimer.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, returnsFTIMER_ERR_MPI_INCONwith 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 masterguards around all timer operations inftimer_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_parallelcontrol 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
fprettifyonsrc/,tests/, andexamples/; 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, anddocs/semantics.mdall match the code undersrc/
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/, andexamples/ - 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)