Files
dominic fc8a082c60 feat: Add ScopedPauseTiming RAII helper (#2157)
* feat: Add ScopedPauseTiming RAII helper

Adds a new `benchmark::ScopedPauseTiming` class that provides a convenient
RAII-style mechanism for pausing and resuming benchmark timers.

This is less error-prone than manually calling `PauseTiming` and
`ResumeTiming`, as it guarantees that the timer is resumed when the
scope is exited.

- Added `ScopedPauseTiming` to `include/benchmark/state.h`.
- Added a new test `test/scoped_pause_test.cc` to verify the
  functionality and prevent regressions.
- Updated `test/CMakeLists.txt` to include the new test.
- Added documentation for the new feature in `docs/user_guide.md`.

* Bump astral-sh/setup-uv from 7.6.0 to 8.0.0 (#2160)

Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 7.6.0 to 8.0.0.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/37802adc94f370d6bfd71619e3f0bf239e1f3b78...cec208311dfd045dd5311c1add060b2062131d57)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump lukka/get-cmake from 4.3.0 to 4.3.1 (#2159)

Bumps [lukka/get-cmake](https://github.com/lukka/get-cmake) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/lukka/get-cmake/releases)
- [Changelog](https://github.com/lukka/get-cmake/blob/main/RELEASE_PROCESS.md)
- [Commits](https://github.com/lukka/get-cmake/compare/b78306120111dc2522750771cfd09ee7ca723687...ea83089aa35e08e459464341fe24ad024ee2466f)

---
updated-dependencies:
- dependency-name: lukka/get-cmake
  dependency-version: 4.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dominic <510002+dmah42@users.noreply.github.com>

* Bump numpy from 2.4.3 to 2.4.4 in /tools (#2158)

Bumps [numpy](https://github.com/numpy/numpy) from 2.4.3 to 2.4.4.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v2.4.3...v2.4.4)

---
updated-dependencies:
- dependency-name: numpy
  dependency-version: 2.4.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dominic <510002+dmah42@users.noreply.github.com>

* handle move operators

* Use manual time instead of real time for better repeatability

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-02 09:57:26 +01:00

283 lines
7.1 KiB
C++

// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BENCHMARK_STATE_H_
#define BENCHMARK_STATE_H_
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4251 4324)
#endif
#include <cassert>
#include <string>
#include <vector>
#include "benchmark/counter.h"
#include "benchmark/macros.h"
#include "benchmark/statistics.h"
#include "benchmark/types.h"
namespace benchmark {
namespace internal {
class BenchmarkInstance;
class ThreadTimer;
class ThreadManager;
class PerfCountersMeasurement;
} // namespace internal
class ProfilerManager;
class BENCHMARK_EXPORT BENCHMARK_INTERNAL_CACHELINE_ALIGNED State {
public:
struct StateIterator;
friend struct StateIterator;
inline BENCHMARK_ALWAYS_INLINE StateIterator begin();
inline BENCHMARK_ALWAYS_INLINE StateIterator end();
inline bool KeepRunning();
inline bool KeepRunningBatch(IterationCount n);
void PauseTiming();
void ResumeTiming();
void SkipWithMessage(const std::string& msg);
void SkipWithError(const std::string& msg);
bool skipped() const { return internal::NotSkipped != skipped_; }
bool error_occurred() const { return internal::SkippedWithError == skipped_; }
void SetIterationTime(double seconds);
BENCHMARK_ALWAYS_INLINE
void SetBytesProcessed(int64_t bytes) {
counters["bytes_per_second"] =
Counter(static_cast<double>(bytes), Counter::kIsRate, Counter::kIs1024);
}
BENCHMARK_ALWAYS_INLINE
int64_t bytes_processed() const {
if (counters.find("bytes_per_second") != counters.end())
return static_cast<int64_t>(counters.at("bytes_per_second"));
return 0;
}
BENCHMARK_ALWAYS_INLINE
void SetComplexityN(ComplexityN complexity_n) {
complexity_n_ = complexity_n;
}
BENCHMARK_ALWAYS_INLINE
ComplexityN complexity_length_n() const { return complexity_n_; }
BENCHMARK_ALWAYS_INLINE
void SetItemsProcessed(int64_t items) {
counters["items_per_second"] =
Counter(static_cast<double>(items), benchmark::Counter::kIsRate);
}
BENCHMARK_ALWAYS_INLINE
int64_t items_processed() const {
if (counters.find("items_per_second") != counters.end())
return static_cast<int64_t>(counters.at("items_per_second"));
return 0;
}
void SetLabel(const std::string& label);
BENCHMARK_ALWAYS_INLINE
int64_t range(std::size_t pos = 0) const {
assert(range_.size() > pos);
return range_[pos];
}
BENCHMARK_DEPRECATED_MSG("use 'range(0)' instead")
int64_t range_x() const { return range(0); }
BENCHMARK_DEPRECATED_MSG("use 'range(1)' instead")
int64_t range_y() const { return range(1); }
BENCHMARK_ALWAYS_INLINE
int threads() const { return threads_; }
BENCHMARK_ALWAYS_INLINE
int thread_index() const { return thread_index_; }
BENCHMARK_ALWAYS_INLINE
IterationCount iterations() const {
if (BENCHMARK_BUILTIN_EXPECT(!started_, false)) {
return 0;
}
return max_iterations - total_iterations_ + batch_leftover_;
}
BENCHMARK_ALWAYS_INLINE
std::string name() const { return name_; }
size_t range_size() const { return range_.size(); }
private:
IterationCount total_iterations_;
IterationCount batch_leftover_;
public:
const IterationCount max_iterations;
private:
bool started_;
bool finished_;
internal::Skipped skipped_;
std::vector<int64_t> range_;
ComplexityN complexity_n_;
public:
UserCounters counters;
private:
State(std::string name, IterationCount max_iters,
const std::vector<int64_t>& ranges, int thread_i, int n_threads,
internal::ThreadTimer* timer, internal::ThreadManager* manager,
internal::PerfCountersMeasurement* perf_counters_measurement,
ProfilerManager* profiler_manager);
void StartKeepRunning();
inline bool KeepRunningInternal(IterationCount n, bool is_batch);
void FinishKeepRunning();
const std::string name_;
const int thread_index_;
const int threads_;
internal::ThreadTimer* const timer_;
internal::ThreadManager* const manager_;
internal::PerfCountersMeasurement* const perf_counters_measurement_;
ProfilerManager* const profiler_manager_;
friend class internal::BenchmarkInstance;
};
inline BENCHMARK_ALWAYS_INLINE bool State::KeepRunning() {
return KeepRunningInternal(1, /*is_batch=*/false);
}
inline BENCHMARK_ALWAYS_INLINE bool State::KeepRunningBatch(IterationCount n) {
return KeepRunningInternal(n, /*is_batch=*/true);
}
inline BENCHMARK_ALWAYS_INLINE bool State::KeepRunningInternal(IterationCount n,
bool is_batch) {
assert(n > 0);
assert(is_batch || n == 1);
if (BENCHMARK_BUILTIN_EXPECT(total_iterations_ >= n, true)) {
total_iterations_ -= n;
return true;
}
if (!started_) {
StartKeepRunning();
if (!skipped() && total_iterations_ >= n) {
total_iterations_ -= n;
return true;
}
}
if (is_batch && total_iterations_ != 0) {
batch_leftover_ = n - total_iterations_;
total_iterations_ = 0;
return true;
}
FinishKeepRunning();
return false;
}
struct State::StateIterator {
struct BENCHMARK_UNUSED Value {};
typedef std::forward_iterator_tag iterator_category;
typedef Value value_type;
typedef Value reference;
typedef Value pointer;
typedef std::ptrdiff_t difference_type;
private:
friend class State;
BENCHMARK_ALWAYS_INLINE
StateIterator() : cached_(0), parent_() {}
BENCHMARK_ALWAYS_INLINE
explicit StateIterator(State* st)
: cached_(st->skipped() ? 0 : st->max_iterations), parent_(st) {}
public:
BENCHMARK_ALWAYS_INLINE
Value operator*() const { return Value(); }
BENCHMARK_ALWAYS_INLINE
StateIterator& operator++() {
assert(cached_ > 0);
--cached_;
return *this;
}
BENCHMARK_ALWAYS_INLINE
bool operator!=(StateIterator const&) const {
if (BENCHMARK_BUILTIN_EXPECT(cached_ != 0, true)) return true;
parent_->FinishKeepRunning();
return false;
}
private:
IterationCount cached_;
State* const parent_;
};
inline BENCHMARK_ALWAYS_INLINE State::StateIterator State::begin() {
return StateIterator(this);
}
inline BENCHMARK_ALWAYS_INLINE State::StateIterator State::end() {
StartKeepRunning();
return StateIterator();
}
class ScopedPauseTiming {
public:
explicit ScopedPauseTiming(State& state) : state_(state) {
state_.PauseTiming();
}
~ScopedPauseTiming() { state_.ResumeTiming(); }
ScopedPauseTiming(const ScopedPauseTiming&) = delete;
void operator=(const ScopedPauseTiming&) = delete;
ScopedPauseTiming(ScopedPauseTiming&&) = delete;
void operator=(ScopedPauseTiming&&) = delete;
private:
State& state_;
};
} // namespace benchmark
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif // BENCHMARK_STATE_H_