Repository: google/benchmark
File: test/manual_threading_test.cc (C++)

Code before:
  }
}

}  // namespace

// ========================================================================= //
// --------------------------- TEST CASES BEGIN ---------------------------- //
// ========================================================================= //

// ========================================================================= //
// BM_ManualThreading
// Creation of threads is done before the start of the measurement,
// joining after the finish of the measurement.
class ManualThreadRunner : public benchmark::ThreadRunnerBase {
 public:
  explicit ManualThreadRunner(int num_threads)
      : pool(static_cast<size_t>(num_threads - 1)) {}

  void RunThreads(const std::function<void(int)>& fn) final {
    for (std::size_t ti = 0; ti < pool.size(); ++ti) {
      pool[ti] = std::thread(fn, static_cast<int>(ti + 1));
    }

    fn(0);

    for (std::thread& thread : pool) {
      thread.join();
    }
  }

 private:
  std::vector<std::thread> pool;
};

void BM_ManualThreading(benchmark::State& state) {
  for (auto _ : state) {
    MyBusySpinwait();
    state.SetIterationTime(time_frame_in_sec);
  }
  state.counters["invtime"] =
      benchmark::Counter{1, benchmark::Counter::kIsRate};
}

BENCHMARK(BM_ManualThreading)
    ->Iterations(1)
    ->ThreadRunner([](int num_threads) {
      return std::make_unique<ManualThreadRunner>(num_threads);
    })
    ->Threads(1);
BENCHMARK(BM_ManualThreading)
    ->Iterations(1)

Review comment (suggestion):
You'd want to actually test that the library is actually using your custom runner,
and not the default one. (i.e. add some file-level static variables, increment them in `RunThreads`,
and check them afterwards)

Code after:
}

int numRunThreadsCalled_ = 0;

class ManualThreadRunner : public benchmark::ThreadRunnerBase {
 public:
  explicit ManualThreadRunner(int num_threads)
      : pool(static_cast<size_t>(num_threads - 1)) {}

  void RunThreads(const std::function<void(int)>& fn) final {
    for (std::size_t ti = 0; ti < pool.size(); ++ti) {
      pool[ti] = std::thread(fn, static_cast<int>(ti + 1));
    }

    fn(0);

    for (std::thread& thread : pool) {
      thread.join();
    }

    ++numRunThreadsCalled_;
  }

 private:
  std::vector<std::thread> pool;
};

// ========================================================================= //
// --------------------------- TEST CASES BEGIN ---------------------------- //
// ========================================================================= //

// ========================================================================= //
// BM_ManualThreading
// Creation of threads is done before the start of the measurement,
// joining after the finish of the measurement.
void BM_ManualThreading(benchmark::State& state) {
  for (auto _ : state) {
    MyBusySpinwait();
    state.SetIterationTime(time_frame_in_sec);
  }
  state.counters["invtime"] =
      benchmark::Counter{1, benchmark::Counter::kIsRate};
}

}  // end namespace

BENCHMARK(BM_ManualThreading)
    ->Iterations(1)
    ->ThreadRunner([](int num_threads) {
      return std::make_unique<ManualThreadRunner>(num_threads);
    })