OpenPFC / Build a simulation

Measure a region with a defined boundary

Time actual local work, accumulate laps and reduce a maximum without confusing it with an end-to-end benchmark.

Download runnable lesson (.zip)

Includes the model, inputs, required headers and a short run guide.

A timer is useful only when its boundaries are clear. This refactors the old sleeping FFT placeholder into three actual distributed cosine sums. The environment initializes MPI before the timer calls MPI_Wtime.

Time the local work, then communicate

for (int lap = 0; lap < 3; ++lap) {
  double local = 0, global = 0;
  timer.tic();
  for (int j = comm.rank(); j < n; j += comm.size())
    local += std::cos(2 * std::numbers::pi * j / n);
  timer.toc();
  MPI_Allreduce(&local, &global, 1, MPI_DOUBLE, MPI_SUM, comm);
  checksum = std::max(checksum, std::abs(global));
}
const double local_seconds = timer.duration();
double maximum = 0, total = 0;
MPI_Allreduce(&local_seconds, &maximum, 1, MPI_DOUBLE, MPI_MAX, comm);
MPI_Allreduce(&local_seconds, &total, 1, MPI_DOUBLE, MPI_SUM, comm);

Each rank owns cyclic sample indices. The numerical checksum has the independent exact value \(\sum_{j=0}^{N-1}\cos(2\pi j/N)=0\), for \(N=4096\). The reduction is outside the timed region. duration() accumulates the three completed laps on each rank; the final reductions report the maximum and mean of those accumulated durations.

The maximum local work duration is not the elapsed time of the whole run: it excludes the reductions, startup and output. The code makes no speedup claim. It also does not compare unsynchronised absolute clock readings between ranks; each elapsed interval is formed on its own rank.

Check arithmetic without asserting a speed

openpfc run model.cpp input.json --launcher=none
openpfc run model.cpp input.json --ranks=2 --launcher=mpirun

The empty input is unused. The CSV reports rank count, lap count, checksum, maximum duration and mean duration. The checks require finite nonnegative durations, maximum at least mean, and the cosine-sum identity. They do not assert that a particular machine finishes in a fixed number of microseconds.

Change n, then move the reduction inside the timing boundary and explain what the new quantity includes. Call reset() before a fresh measurement series; every toc() must have a matching tic(). Continue with structured profiling, which gives names and nesting to regions across accepted frames. The timer header defines lap accumulation.

The complete C++ model and starting input are available with this lesson.