Repository: google/benchmark
File: src/perf_counters.h (C/C++)

Code before:
  std::vector<int> counter_ids_;
  const std::vector<std::string> counter_names_;
  const bool is_valid_;
};

// Typical usage of the above primitives.
class PerfCountersMeasurement final {
 public:
  PerfCountersMeasurement(PerfCounters&& c)
      : counters_(std::move(c)),
        start_values_(counters_.IsValid() ? counters_.names().size() : 0),
        end_values_(counters_.IsValid() ? counters_.names().size() : 0) {}

  bool IsValid() const { return counters_.IsValid(); }

  BENCHMARK_ALWAYS_INLINE void Start() {
    assert(IsValid());
    // Tell the compiler to not move instructions above/below where we take
    // the snapshot.
    ClobberMemory();
    valid_read_ &= counters_.Snapshot(&start_values_);
    ClobberMemory();
  }

  BENCHMARK_ALWAYS_INLINE
  std::pair<std::vector<std::pair<std::string, double>>, bool>
  StopAndGetMeasurements() {
    assert(IsValid());
    // Tell the compiler to not move instructions above/below where we take
    // the snapshot.
    ClobberMemory();
    valid_read_ &= counters_.Snapshot(&end_values_);
    ClobberMemory();

    std::vector<std::pair<std::string, double>> ret;
    for (size_t i = 0; i < counters_.names().size(); ++i) {
      double measurement = static_cast<double>(end_values_[i]) -
                           static_cast<double>(start_values_[i]);
      ret.push_back({counters_.names()[i], measurement});
    }
    return std::make_pair(ret, valid_read_);
  }

 private:
  bool valid_read_ = true;
  PerfCounters counters_;
  PerfCounterValues start_values_;
  PerfCounterValues end_values_;
};

BENCHMARK_UNUSED static bool perf_init_anchor = PerfCounters::Initialize();

Review comment (suggestion):
i think this return type is getting to the point where we should consider a separate data structure.  i'd also be ok with a boolean return for validity and an out param for the vector of measurements.

Code after:
  PerfCounters() = default;

  void CloseCounters() const;

  std::vector<int> counter_ids_;
  std::vector<std::string> counter_names_;
};

// Typical usage of the above primitives.
class PerfCountersMeasurement final {
 public:
  PerfCountersMeasurement(const std::vector<std::string>& counter_names);
  ~PerfCountersMeasurement();

  // The only way to get to `counters_` is after ctor-ing a
  // `PerfCountersMeasurement`, which means that `counters_`'s state is, here,
  // decided (either invalid or valid) and won't change again even if a ctor is
  // concurrently running with this. This is preferring efficiency to
  // maintainability, because the address of the static can be known at compile
  // time.
  bool IsValid() const { return counters_.IsValid(); }

  BENCHMARK_ALWAYS_INLINE void Start() {
    assert(IsValid());
    // Tell the compiler to not move instructions above/below where we take
    // the snapshot.
    ClobberMemory();
    valid_read_ &= counters_.Snapshot(&start_values_);
    ClobberMemory();
  }

  BENCHMARK_ALWAYS_INLINE bool Stop(
      std::vector<std::pair<std::string, double>>& measurements) {
    assert(IsValid());
    // Tell the compiler to not move instructions above/below where we take
    // the snapshot.
    ClobberMemory();
    valid_read_ &= counters_.Snapshot(&end_values_);
    ClobberMemory();

    for (size_t i = 0; i < counters_.names().size(); ++i) {
      double measurement = static_cast<double>(end_values_[i]) -
                           static_cast<double>(start_values_[i]);
      measurements.push_back({counters_.names()[i], measurement});
    }

    return valid_read_;
  }

 private:
  static Mutex mutex_;