Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
2501class LinuxPerformanceCounters {2502public:2503struct Target {2504Target(uint64_t* targetValue_, bool correctMeasuringOverhead_, bool correctLoopOverhead_)2505: targetValue(targetValue_)2506, correctMeasuringOverhead(correctMeasuringOverhead_)2507, correctLoopOverhead(correctLoopOverhead_) {}2509uint64_t* targetValue{}; // NOLINT(misc-non-private-member-variables-in-classes)2510bool correctMeasuringOverhead{}; // NOLINT(misc-non-private-member-variables-in-classes)2511bool correctLoopOverhead{}; // NOLINT(misc-non-private-member-variables-in-classes)2512};2514LinuxPerformanceCounters() = default;2515LinuxPerformanceCounters(LinuxPerformanceCounters const&) = delete;2516LinuxPerformanceCounters(LinuxPerformanceCounters&&) = delete;2517LinuxPerformanceCounters& operator=(LinuxPerformanceCounters const&) = delete;2518LinuxPerformanceCounters& operator=(LinuxPerformanceCounters&&) = delete;2519~LinuxPerformanceCounters();2521// quick operation2522inline void start() {}2524inline void stop() {}2526bool monitor(perf_sw_ids swId, Target target);2527bool monitor(perf_hw_id hwId, Target target);2529ANKERL_NANOBENCH(NODISCARD) bool hasError() const noexcept {2530return mHasError;2531}2533// Just reading data is faster than enable & disabling.2534// we subtract data ourselves.2535inline void beginMeasure() {2536if (mHasError) {2537return;2538}2540// NOLINTNEXTLINE(hicpp-signed-bitwise,cppcoreguidelines-pro-type-vararg)2541mHasError = -1 == ioctl(mFd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);2542if (mHasError) {2543return;2544}2546// NOLINTNEXTLINE(hicpp-signed-bitwise,cppcoreguidelines-pro-type-vararg)2547mHasError = -1 == ioctl(mFd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);2548}2550inline void endMeasure() {2551if (mHasError) {2552return;2553}2555// NOLINTNEXTLINE(hicpp-signed-bitwise,cppcoreguidelines-pro-type-vararg)2556mHasError = (-1 == ioctl(mFd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP));2557if (mHasError) {2558return;2559}2561auto const numBytes = sizeof(uint64_t) * mCounters.size();2562auto ret = read(mFd, mCounters.data(), numBytes);2563mHasError = ret != static_cast<ssize_t>(numBytes);2564}2566void updateResults(uint64_t numIters);2568// rounded integer division2569template <typename T>2570static inline T divRounded(T a, T divisor) {2571return (a + divisor / 2) / divisor;2572}2574ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")2575static inline uint32_t mix(uint32_t x) noexcept {2576x ^= x << 13U;2577x ^= x >> 17U;2578x ^= x << 5U;2579return x;2580}2582template <typename Op>2583ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")2584void calibrate(Op&& op) {2585// clear current calibration data,2586for (auto& v : mCalibratedOverhead) {2587v = UINT64_C(0);2588}2590// create new calibration data2591auto newCalibration = mCalibratedOverhead;2592for (auto& v : newCalibration) {2593v = (std::numeric_limits<uint64_t>::max)();2594}2595for (size_t iter = 0; iter < 100; ++iter) {2596beginMeasure();2597op();2598endMeasure();2599if (mHasError) {2600return;2601}2603for (size_t i = 0; i < newCalibration.size(); ++i) {2604auto diff = mCounters[i];2605if (newCalibration[i] > diff) {2606newCalibration[i] = diff;2607}2608}2609}2611mCalibratedOverhead = std::move(newCalibration);2613{2614// calibrate loop overhead. For branches & instructions this makes sense, not so much for everything else like cycles.2615// marsaglia's xorshift: mov, sal/shr, xor. Times 3.2616// This has the nice property that the compiler doesn't seem to be able to optimize multiple calls any further.2617// see https://godbolt.org/z/49RVQ52618uint64_t const numIters = 100000U + (std::random_device{}() & 3U);2619uint64_t n = numIters;2620uint32_t x = 1234567;2622beginMeasure();2623while (n-- > 0) {2624x = mix(x);2625}2626endMeasure();2627detail::doNotOptimizeAway(x);2628auto measure1 = mCounters;2630n = numIters;2631beginMeasure();2632while (n-- > 0) {2633// we now run *twice* so we can easily calculate the overhead2634x = mix(x);2635x = mix(x);2636}2637endMeasure();2638detail::doNotOptimizeAway(x);2639auto measure2 = mCounters;2641for (size_t i = 0; i < mCounters.size(); ++i) {2642// factor 2 because we have two instructions per loop2643auto m1 = measure1[i] > mCalibratedOverhead[i] ? measure1[i] - mCalibratedOverhead[i] : 0;2644auto m2 = measure2[i] > mCalibratedOverhead[i] ? measure2[i] - mCalibratedOverhead[i] : 0;2645auto overhead = m1 * 2 > m2 ? m1 * 2 - m2 : 0;2647mLoopOverhead[i] = divRounded(overhead, numIters);2648}2649}2650}2652private:2653bool monitor(uint32_t type, uint64_t eventid, Target target);2655std::map<uint64_t, Target> mIdToTarget{};2657// start with minimum size of 3 for read_format2658std::vector<uint64_t> mCounters = std::vector<uint64_t>(3);2659std::vector<uint64_t> mCalibratedOverhead = std::vector<uint64_t>(3);2660std::vector<uint64_t> mLoopOverhead = std::vector<uint64_t>(3);2662uint64_t mTimeEnabledNanos = 0;2663uint64_t mTimeRunningNanos = 0;2664int mFd = -1;2665bool mHasError = false;2666};2667ANKERL_NANOBENCH(IGNORE_PADDED_POP)2669LinuxPerformanceCounters::~LinuxPerformanceCounters() {2670if (-1 != mFd) {2671close(mFd);2672}2673}2675bool LinuxPerformanceCounters::monitor(perf_sw_ids swId, LinuxPerformanceCounters::Target target) {2676return monitor(PERF_TYPE_SOFTWARE, swId, target);2677}2679bool LinuxPerformanceCounters::monitor(perf_hw_id hwId, LinuxPerformanceCounters::Target target) {2680return monitor(PERF_TYPE_HARDWARE, hwId, target);2681}2683// overflow is ok, it's checked2684ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")2685void LinuxPerformanceCounters::updateResults(uint64_t numIters) {2686// clear old data2687for (auto& id_value : mIdToTarget) {2688*id_value.second.targetValue = UINT64_C(0);2689}2691if (mHasError) {2692return;2693}2695mTimeEnabledNanos = mCounters[1] - mCalibratedOverhead[1];2696mTimeRunningNanos = mCounters[2] - mCalibratedOverhead[2];2698for (uint64_t i = 0; i < mCounters[0]; ++i) {2699auto idx = static_cast<size_t>(3 + i * 2 + 0);2700auto id = mCounters[idx + 1U];2702auto it = mIdToTarget.find(id);2703if (it != mIdToTarget.end()) {2705auto& tgt = it->second;2706*tgt.targetValue = mCounters[idx];2707if (tgt.correctMeasuringOverhead) {2708if (*tgt.targetValue >= mCalibratedOverhead[idx]) {2709*tgt.targetValue -= mCalibratedOverhead[idx];2710} else {2711*tgt.targetValue = 0U;2712}2713}2714if (tgt.correctLoopOverhead) {2715auto correctionVal = mLoopOverhead[idx] * numIters;2716if (*tgt.targetValue >= correctionVal) {2717*tgt.targetValue -= correctionVal;2718} else {2719*tgt.targetValue = 0U;2720}2721}2722}2723}2724}2726bool LinuxPerformanceCounters::monitor(uint32_t type, uint64_t eventid, Target target) {2727*target.targetValue = (std::numeric_limits<uint64_t>::max)();2728if (mHasError) {2729return false;2730}2732auto pea = perf_event_attr();2733std::memset(&pea, 0, sizeof(perf_event_attr));2734pea.type = type;2735pea.size = sizeof(perf_event_attr);2736pea.config = eventid;2737pea.disabled = 1; // start counter as disabled2738pea.exclude_kernel = 1;2739pea.exclude_hv = 1;2741// NOLINTNEXTLINE(hicpp-signed-bitwise)2742pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;2744const int pid = 0; // the current process2745const int cpu = -1; // all CPUs2746# if defined(PERF_FLAG_FD_CLOEXEC) // since Linux 3.142747const unsigned long flags = PERF_FLAG_FD_CLOEXEC;2748# else2749const unsigned long flags = 0;2750# endif
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.