pub struct Group(/* private fields */);Expand description
A group of counters that can be managed as a unit.
A Group represents a group of Counters that can be enabled,
disabled, reset, or read as a single atomic operation. This is necessary if
you want to compare counter values, produce ratios, and so on, since those
operations are only meaningful on counters that cover exactly the same
period of execution.
A Counter is placed in a group when it is created via the
Builder::build_with_group method. A Group’s read method returns
values of all its member counters at once as a GroupData value, which
can be indexed by Counter to retrieve a specific value.
The lifetime of a Group and its associated Counters are independent:
you can drop them in any order and they will continue to work. A Counter
will continue to work after the Group is dropped. If a Counter is
dropped first then it will simply be removed from the Group.
Enabling or disabling a Group affects each Counter that belongs to it.
Subsequent reads from the Counter will not reflect activity while the
Group was disabled, unless the Counter is re-enabled individually.
§Limits on group size
Hardware counters are implemented using special-purpose registers on the processor, of which there are only a fixed number. (For example, an Intel high-end laptop processor from 2015 has four such registers per virtual processor.) Without using groups, if you request more hardware counters than the processor can actually support, a complete count isn’t possible, but the kernel will rotate the processor’s real registers amongst the measurements you’ve requested to at least produce a sample.
But since the point of a counter group is that its members all cover exactly
the same period of time, this tactic can’t be applied to support large
groups. If the kernel cannot schedule a group, its counters remain zero. I
think you can detect this situation by comparing the group’s
time_enabled and time_running values. If the pinned option is
set then you will also be able to detect this by read returning an error
with kind UnexpectedEof.
According to the perf_list(1) man page, you may be able to free up a
hardware counter by disabling the kernel’s NMI watchdog, which reserves one
for detecting kernel hangs:
$ echo 0 > /proc/sys/kernel/nmi_watchdogYou can reenable the watchdog when you’re done like this:
$ echo 1 > /proc/sys/kernel/nmi_watchdog§Examples
Compute the average cycles-per-instruction (CPI) for a call to println!:
use perf_event::events::Hardware;
use perf_event::{Builder, Group};
let mut group = Group::new()?;
let cycles = group.add(&Builder::new(Hardware::CPU_CYCLES))?;
let insns = group.add(&Builder::new(Hardware::INSTRUCTIONS))?;
let vec = (0..=51).collect::<Vec<_>>();
group.enable()?;
println!("{:?}", vec);
group.disable()?;
let counts = group.read()?;
println!(
"cycles / instructions: {} / {} ({:.2} cpi)",
counts[&cycles],
counts[&insns],
(counts[&cycles] as f64 / counts[&insns] as f64)
);Implementations§
Source§impl Group
impl Group
Sourcepub fn new() -> Result<Group>
pub fn new() -> Result<Group>
Construct a new, empty Group.
The resulting Group is only suitable for observing the current process
on any CPU. If you need to build a Group with different settings you
will need to use Builder::build_group.
Sourcepub fn builder() -> Builder<'static>
pub fn builder() -> Builder<'static>
Construct a Builder preconfigured for creating a Group.
Specifically, this creates a builder with the Software::DUMMY event
and with read_format set to GROUP | ID | TOTAL_TIME_ENABLED | TOTAL_TIME_RUNNING. If you override read_format you will need to
ensure that ReadFormat::GROUP is set, otherwise build_group will
return an error.
Note that any counter added to this group must observe the same set of CPUs and processes as the group itself. That means if you configure the group to observe a single CPU then the members of the group must also be configured to only observe a single CPU, the same applies when choosing target processes. Failing to follow this will result in an error when adding the counter to the group.
Sourcepub fn as_counter(&self) -> &Counter
pub fn as_counter(&self) -> &Counter
Access the internal counter for this group.
Sourcepub fn as_counter_mut(&mut self) -> &mut Counter
pub fn as_counter_mut(&mut self) -> &mut Counter
Mutably access the internal counter for this group.
Sourcepub fn into_counter(self) -> Counter
pub fn into_counter(self) -> Counter
Convert this Group into its internal counter.
Sourcepub fn add(&mut self, builder: &Builder<'_>) -> Result<Counter>
pub fn add(&mut self, builder: &Builder<'_>) -> Result<Counter>
Construct a new counter as a part of this group.
§Example
use perf_event::events::Hardware;
use perf_event::{Builder, Group};
let mut group = Group::new()?;
let counter = group.add(&Builder::new(Hardware::INSTRUCTIONS).any_cpu());Sourcepub fn read(&mut self) -> Result<GroupData>
pub fn read(&mut self) -> Result<GroupData>
Return the values of all the Counters in this Group as a
GroupData value.
A GroupData value is a map from specific Counters to their values.
You can find a specific Counter’s value by indexing:
use perf_event::{Builder, Group};
let mut group = Group::new()?;
let counter1 = Builder::new(RHOMBUS_INCLINATIONS).build_with_group(&mut group)?;
let counter2 = Builder::new(TAXI_MEDALLIONS).build_with_group(&mut group)?;
// ...
let counts = group.read()?;
println!(
"Rhombus inclinations per taxi medallion: {} / {} ({:.0}%)",
counts[&counter1],
counts[&counter2],
(counts[&counter1] as f64 / counts[&counter2] as f64) * 100.0
);