-
Notifications
You must be signed in to change notification settings - Fork 80
Record instructions retired alongside time in TIME_TRACE (depends on #928) #929
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
quickbeam123
wants to merge
1
commit into
martin-timetrace-race
Choose a base branch
from
martin-timetrace-instructions
base: martin-timetrace-race
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| /* | ||
| * This file is part of the source code of the software program | ||
| * Vampire. It is protected by applicable | ||
| * copyright laws. | ||
| * | ||
| * This source code is distributed under the licence found here | ||
| * https://vprover.github.io/license.html | ||
| * and in the source directory | ||
| */ | ||
| /** | ||
| * @file PerfInstructions.hpp | ||
| * Reading the hardware "instructions retired" counter cheaply, from user space. | ||
| * | ||
| * Lib/Timer.cpp opens a PERF_COUNT_HW_INSTRUCTIONS event and reads it with | ||
| * read(PERF_FD), which costs a syscall (measured: ~560ns). That is fine for the | ||
| * timer thread's periodic limit check, but far too slow for a counter we want to | ||
| * sample on every TIME_TRACE scope. | ||
| * | ||
| * The fast path is to mmap the same file descriptor -- which yields a | ||
| * struct perf_event_mmap_page -- and read the counter register directly with the | ||
| * rdpmc instruction. Measured on the reference server: 8.9ns, against 27.5ns for | ||
| * clock_gettime(CLOCK_MONOTONIC), i.e. cheaper than the clock read the profiler | ||
| * already does. | ||
| * | ||
| * IMPORTANT: instructionCount() may only be called from the thread that | ||
| * Timer::reinitialise() ran on -- in practice, Vampire's main thread. rdpmc reads | ||
| * the performance counter register of whatever CPU the *caller* is running on, | ||
| * which for any other thread is not this event at all. The `index` check below | ||
| * catches the common case (returning -1 so the caller can fall back), but it is not | ||
| * a guarantee, so do not call this from timer_thread; use | ||
| * Timer::updateInstructionCount() there instead. | ||
| * | ||
| * This header pulls in <linux/perf_event.h>, so include it only where it is needed | ||
| * rather than from a widely-included header. | ||
| */ | ||
|
|
||
| #ifndef __PerfInstructions__ | ||
| #define __PerfInstructions__ | ||
|
|
||
| #include "Lib/Portability.hpp" | ||
|
|
||
| #if VAMPIRE_PERF_EXISTS | ||
| #include <cstdint> | ||
| #include <linux/perf_event.h> | ||
| #endif | ||
|
|
||
| namespace Lib { | ||
| namespace Timer { | ||
|
|
||
| #if VAMPIRE_PERF_EXISTS | ||
|
|
||
| /** The mmap'd metadata page of the perf event, or nullptr when unavailable. | ||
| * Set up by Timer::reinitialise(). */ | ||
| extern perf_event_mmap_page *PERF_MMAP_PAGE; | ||
|
|
||
| /** Whether instructionCount() can return anything meaningful at all. */ | ||
| bool instructionCountingAvailable(); | ||
|
|
||
| #if defined(__x86_64__) || defined(__i386__) | ||
| inline uint64_t rdpmc(uint32_t counter) | ||
| { | ||
| uint32_t low, high; | ||
| __asm__ __volatile__("rdpmc" : "=a"(low), "=d"(high) : "c"(counter)); | ||
| return (static_cast<uint64_t>(high) << 32) | low; | ||
| } | ||
| #define VAMPIRE_RDPMC_EXISTS 1 | ||
| #endif | ||
|
|
||
| #endif // VAMPIRE_PERF_EXISTS | ||
|
|
||
| /** | ||
| * User-space instructions retired by this thread since the counter was reset, | ||
| * or -1 if that cannot be determined right now. | ||
| * | ||
| * Callers should treat -1 as "no measurement", not as a count. | ||
| */ | ||
| inline long long instructionCount() | ||
| { | ||
| #if VAMPIRE_PERF_EXISTS && defined(VAMPIRE_RDPMC_EXISTS) | ||
| perf_event_mmap_page *pc = PERF_MMAP_PAGE; | ||
| if (!pc) | ||
| return -1; | ||
|
|
||
| uint64_t count; | ||
| uint32_t seq, idx; | ||
| int64_t offset; | ||
| uint16_t width; | ||
|
|
||
| do { | ||
| // pc->lock is a seqlock the kernel bumps whenever it reschedules the event, | ||
| // which is exactly when index and offset change under us | ||
| seq = pc->lock; | ||
| __atomic_signal_fence(__ATOMIC_SEQ_CST); | ||
|
|
||
| idx = pc->index; // 0: the event is not on this CPU's PMU at the moment | ||
| offset = pc->offset; // what it counted during previous schedulings | ||
| width = pc->pmc_width; | ||
|
|
||
| if (!pc->cap_user_rdpmc || !idx || width == 0 || width > 64) | ||
| return -1; | ||
|
|
||
| count = rdpmc(idx - 1); | ||
| // the hardware register is narrower than 64 bits (typically 48); sign-extend | ||
| count <<= 64 - width; | ||
| count = static_cast<uint64_t>(static_cast<int64_t>(count) >> (64 - width)); | ||
| count += offset; | ||
|
|
||
| __atomic_signal_fence(__ATOMIC_SEQ_CST); | ||
| } while (pc->lock != seq); | ||
|
|
||
| return static_cast<long long>(count); | ||
| #else | ||
| return -1; | ||
| #endif | ||
| } | ||
|
|
||
| } // namespace Timer | ||
| } // namespace Lib | ||
|
|
||
| #endif // __PerfInstructions__ | ||
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is rumoured GCC has an intrinsic for this, but I couldn't find anything quickly. In any event, this should be
#ifdef-guarded for GCC/Clang so that__asm__doesn't cause a compiler error.