1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[allow(dead_code)]
#[allow(non_upper_case_globals)]
mod bindings;
pub mod counter;
pub mod events_set;
use std::os::raw::{c_int, c_ulong};
use std::sync::atomic::{AtomicU64, Ordering};
use crate::bindings::*;
const fn papi_version_number(maj: u32, min: u32, rev: u32, inc: u32) -> u32 {
(maj << 24) | (min << 16) | (rev << 8) | inc
}
const PAPI_VERSION: u32 = papi_version_number(6, 0, 0, 1);
const PAPI_VER_CURRENT: c_int = (PAPI_VERSION & 0xffff0000) as c_int;
#[link(name = "papi")]
extern "C" {}
#[derive(Debug)]
#[allow(dead_code)]
pub struct PapiError {
code: i32,
}
pub(crate) fn check_error(code: i32) -> Result<(), PapiError> {
if code == (PAPI_OK as i32) {
Ok(())
} else {
Err(PapiError { code })
}
}
static THREAD_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
thread_local! {
static THREAD_INDEX: u64 = THREAD_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
}
extern "C" fn get_thread_id() -> c_ulong {
THREAD_INDEX.with(|id| *id as c_ulong)
}
pub fn initialize(multithread: bool) -> Result<(), PapiError> {
unsafe {
let version = PAPI_library_init(PAPI_VER_CURRENT);
if version != PAPI_VER_CURRENT {
return Err(PapiError { code: version });
}
if multithread {
check_error(PAPI_thread_init(Some(get_thread_id)))?;
}
}
Ok(())
}
pub fn is_initialized() -> bool {
unsafe { check_error(PAPI_is_initialized()).is_ok() }
}
#[derive(PartialEq, Eq)]
pub enum Action {
Retry,
}
#[cfg(test)]
mod tests {
use crate::counter::Counter;
use crate::events_set::EventsSet;
use crate::PAPI_EINVAL;
#[test]
fn test_fib() {
crate::initialize(true).unwrap();
let counters = vec![
Counter::from_name("ix86arch::INSTRUCTION_RETIRED").unwrap(),
Counter::from_name("ix86arch::MISPREDICTED_BRANCH_RETIRED").unwrap(),
];
let mut event_set = EventsSet::new(&counters).unwrap();
let mut second_set = event_set.try_clone().unwrap();
for fv in 1..45 {
event_set.start().unwrap();
second_set.start().unwrap();
let x = fib(fv);
second_set.stop().unwrap();
let counters = event_set.stop().unwrap();
println!(
"Computed fib({}) = {} in {} instructions [mispredicted: {}].",
fv, x, counters[0], counters[1]
);
}
}
fn fib(n: isize) -> isize {
if n < 2 {
1
} else {
fib(n - 1) + fib(n - 2)
}
}
}