hbt/bperf: replace compiler barrier with CPU fence std::atomic_thread_fence(std::memory_order_consume) - #470
Open
luciang wants to merge 1 commit into
Open
Conversation
…_fence(std::memory_order_consume) (facebookincubator#470) Summary: RDPMC is not a serializing instruction. The CPU may reorder instructions around it. https://www.felixcloutier.com/x86/rdpmc > The RDPMC instruction is not a serializing instruction; that is, it does not imply that all the events caused by the preceding instructions have been completed or that events caused by subsequent instructions have not begun. If an exact event count is desired, software must insert a serializing instruction (such as the CPUID instruction) before and/or after the RDPMC instruction. > Performing back-to-back fast reads are not guaranteed to be monotonic. To guarantee monotonicity on back-to-back reads, a serializing instruction must be placed between the two RDPMC instructions. https://www.felixcloutier.com/x86/rdtsc > The RDTSC instruction is not a serializing instruction. It does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the read operation is performed. The following items may guide software seeking to order executions of RDTSC: > If software requires RDTSC to be executed only after all previous instructions have executed and all previous loads are globally visible,1 it can execute LFENCE immediately before RDTSC. > If software requires RDTSC to be executed only after all previous instructions have executed and all previous loads and stores are globally visible, it can execute the sequence MFENCE;LFENCE immediately before RDTSC. > If software requires RDTSC to be executed prior to execution of any subsequent instruction (including any memory accesses), it can execute the sequence LFENCE immediately after RDTSC. Use an serializing instruction per Intel's recommandations. --- Example of differences: ``` #include <atomic> #define barrier() asm volatile("" ::: "memory") extern int x; int f() { barrier(); return x; } int g() { std::atomic_thread_fence(std::memory_order_consume); return x; } ``` ``` f(): push rbp mov rbp, rsp mov eax, DWORD PTR x[rip] pop rbp ret g(): push rbp mov rbp, rsp mov DWORD PTR [rbp-4], 1 lock or QWORD PTR [rsp], 0 nop mov eax, DWORD PTR x[rip] pop rbp ret ``` https://gcc.godbolt.org/z/vGhscPEfG This is also what we're doing in regular perf counters: https://www.internalfb.com/code/fbsource/[b6e9a1b34cb61db1222fc88ba0dc3d3fe60b496b]/fbcode/dyno/cpp/lib/DynoPerfCounter.cpp?lines=500-522 ``` do { seq = mem_->lock; std::atomic_thread_fence(std::memory_order_consume); enabled = mem_->time_enabled; running = mem_->time_running; const uint32_t idx = mem_->index; // May be zero when inactive (eg. multiplexing). if (mem_->cap_user_time) { const uint64_t cyc = __rdtsc(); const uint16_t timeShift = mem_->time_shift; const uint64_t quot = (cyc >> timeShift); const uint64_t rem = cyc & (((uint64_t)1 << timeShift) - 1); const uint64_t delta = mem_->time_offset + (quot * mem_->time_mult) + ((rem * mem_->time_mult) >> timeShift); enabled += delta; running += idx != 0 ? delta : 0; } count = mem_->offset + (idx ? __rdpmc(idx - 1) : 0); // Sign extend. count <<= (64 - mem_->pmc_width); count = static_cast<int64_t>(count) >> (64 - mem_->pmc_width); std::atomic_thread_fence(std::memory_order_consume); } while (seq != mem_->lock); ``` Differential Revision: D86381530
luciang
force-pushed
the
export-D86381530
branch
from
November 6, 2025 05:18
e78b7a5 to
791b303
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
RDPMC is not a serializing instruction.
The CPU may reorder instructions around it.
https://www.felixcloutier.com/x86/rdpmc
https://www.felixcloutier.com/x86/rdtsc
Use an serializing instruction per Intel's recommandations.
Example of differences:
https://gcc.godbolt.org/z/vGhscPEfG
This is also what we're doing in regular perf counters: https://www.internalfb.com/code/fbsource/[b6e9a1b34cb61db1222fc88ba0dc3d3fe60b496b]/fbcode/dyno/cpp/lib/DynoPerfCounter.cpp?lines=500-522
Differential Revision: D86381530