#![feature(asm, stdsimd, test)]
#![cfg_attr(not(test), no_std)]
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
compile_error!(
"The TSC crate only supports the \"x86\" and \"x86_64\" architectures"
);
extern crate test;
use core::ops;
#[cfg(target_arch = "x86")]
use core::arch::x86 as arch;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64 as arch;
pub fn has_invariant_tsc() -> bool {
use self::arch::{has_cpuid, CpuidResult, __cpuid};
if !has_cpuid() {
return false;
}
let CpuidResult { eax: max_basic_leaf, .. } = unsafe { __cpuid(0_u32) };
if max_basic_leaf < 1 {
return false;
}
let CpuidResult { eax: max_extended_leaf, .. } =
unsafe { __cpuid(0x8000_0000_u32) };
if max_extended_leaf < 7 {
return false;
}
let CpuidResult { edx, .. } = unsafe { __cpuid(0x8000_0007_u32) };
edx & (1 << 8) != 0
}
pub struct Start(u64);
impl Start {
pub fn now() -> Self {
unsafe {
let _ = arch::__cpuid(0);
Start(core::mem::transmute(arch::_rdtsc()))
}
}
}
pub struct Stop(u64);
impl Stop {
pub fn now() -> Self {
unsafe {
let mut core: u32 = 0;
let r = arch::__rdtscp(&mut core as *mut _) as u64;
let _ = arch::__cpuid(0);
Stop(r)
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Duration(u64);
impl Duration {
pub fn cycles(self) -> u64 {
self.0
}
pub fn span<R, F: FnOnce() -> R>(f: F) -> (Self, R) {
let start = Start::now();
let result = f();
let stop = Stop::now();
let duration = stop - start;
(duration, result)
}
}
impl ops::Sub<Start> for Stop {
type Output = Duration;
fn sub(self, start: Start) -> Duration {
debug_assert!(
self.0 > start.0,
"stop time instant happened after start time instant"
);
Duration(self.0 - start.0)
}
}
impl ops::Sub<Duration> for Duration {
type Output = Self;
fn sub(self, other: Self) -> Self {
debug_assert!(self.0 > other.0, "subtracting durations overflows");
Duration(self.0 - other.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn start_stop() {
let start = Start::now();
let _noop = test::black_box(0);
let stop = Stop::now();
let dur1 = stop - start;
fn foo() -> i32 {
test::black_box(test::black_box(2_i32).pow(test::black_box(29)))
}
let (dur2, r) = Duration::span(foo);
assert_eq!(r, 536_870_912);
if has_invariant_tsc() {
println!(
"dur2: {} cycles, dur1: {} cycles",
dur2.cycles(),
dur1.cycles()
);
}
}
#[test]
fn print_span_overhead() {
println!(
"span overhead: {} cycles",
Duration::span(|| test::black_box(0)).0.cycles()
);
}
#[test]
fn invariant_tsc() {
if !has_invariant_tsc() {
println!("the cpu does not have an invariant TSC");
}
}
}