Skip to content

Instantly share code, notes, and snippets.

@leiless
Last active May 17, 2023 07:39
Show Gist options
  • Save leiless/22b6cf392622c6e48ca7ae9d4f15e169 to your computer and use it in GitHub Desktop.
Save leiless/22b6cf392622c6e48ca7ae9d4f15e169 to your computer and use it in GitHub Desktop.
_rdtsc ticks per second estimation
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#define N 10
#define SIZE_OF(a) (sizeof(a) / sizeof(*a))
int main(void) {
uint64_t arr[N] = {};
uint64_t a, b;
unsigned int ret;
for (size_t i = 0; i < SIZE_OF(arr); i++) {
a = __builtin_ia32_rdtsc();
ret = sleep(1);
b = __builtin_ia32_rdtsc();
assert(ret == 0);
assert(b >= a);
arr[i] = b - a;
}
uint64_t max = 0;
uint64_t min = (uint64_t) -1;
uint64_t sum = 0;
for (size_t i = 0; i < SIZE_OF(arr); i++) {
printf("[%zu] = %zu\n", i, arr[i]);
if (max < arr[i]) {
max = arr[i];
}
if (min > arr[i]) {
min = arr[i];
}
sum += arr[i];
}
printf("\n");
printf("Min = %zu\n", max);
printf("Max = %zu\n", min);
printf("Max - Min = %zu\n", max - min);
printf("Avg = %zu\n", sum / N);
return 0;
}
@leiless
Copy link
Author

leiless commented May 12, 2023

Example

$ gcc -Wall rdtsc_ticks_per_second.c -O2
$ ./a.out
[0] = 2496208152
[1] = 2495801870
[2] = 2495927943
[3] = 2495907702
[4] = 2495940813
[5] = 2495863155
[6] = 2495805784
[7] = 2495973931
[8] = 2496174094
[9] = 2495927619

Min = 2496208152
Max = 2495801870
Max - Min = 406282
Avg = 2495953106

@leiless
Copy link
Author

leiless commented May 17, 2023

Simple Timer implementation in Rust

use std::fmt::Formatter;

#[derive(Debug, Copy, Clone)]
pub struct Timer {
    tick0: u64,
    pub timeout_secs: u64,
}

lazy_static::lazy_static! {
    static ref TICKS_PER_SECOND: u64 = Timer::init();
}

impl Timer {
    pub fn new(timeout_secs: u64) -> Self {
        Self {
            tick0: Self::_rdtsc(),
            timeout_secs,
        }
    }

    pub fn is_timed_out(&self) -> bool {
        let due_time = self.tick0 + self.timeout_secs * *TICKS_PER_SECOND;
        Self::_rdtsc() >= due_time
    }

    pub fn reset(&mut self, new_timeout_secs: Option<u64>) {
        if let Some(timeout_secs) = new_timeout_secs {
            self.timeout_secs = timeout_secs
        }
        self.tick0 = Self::_rdtsc();
    }

    #[inline(always)]
    fn _rdtsc() -> u64 {
        unsafe {
            #[cfg(target_arch = "x86_64")]
            return core::arch::x86_64::_rdtsc();

            #[cfg(target_arch = "x86")]
            return core::arch::x86::_rdtsc();
        }
    }

    // FIXME: num_cpus::get()
    // https://github.com/seanmonstar/num_cpus
    // https://docs.rs/num_cpus/latest/num_cpus/
    const N_THREAD: usize = 12;

    fn init() -> u64 {
        let sleep_time = std::time::Duration::from_secs(1);
        let mut handles = Vec::with_capacity(Self::N_THREAD);

        for _i in 0..Self::N_THREAD {
            let sleep_time_1 = sleep_time.clone();

            let h = std::thread::spawn(move || {
                let a = Self::_rdtsc();
                std::thread::sleep(sleep_time_1);
                let b = Self::_rdtsc();

                assert!(b >= a);

                b - a
            });
            handles.push(h);
        }

        let mut sum = 0u64;
        for h in handles {
            let ticks_per_second = h.join().unwrap();
            sum += ticks_per_second;
        }

        let avg_ticks_per_second = sum / Self::N_THREAD as u64;
        avg_ticks_per_second
    }
}

impl std::fmt::Display for Timer {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

@leiless
Copy link
Author

leiless commented May 17, 2023

Use Timer

        let now = std::time::SystemTime::now();
        let mut t = timer::Timer::new(1);
        while !t.is_timed_out() {}
        let elapsed = std::time::SystemTime::now().duration_since(now).unwrap();
        eprintln!("Time elapsed #1: {} ms", elapsed.as_millis());

        let now = std::time::SystemTime::now();
        t.reset(None);
        while !t.is_timed_out() {}
        let elapsed = std::time::SystemTime::now().duration_since(now).unwrap();
        eprintln!("Time elapsed #2: {} ms", elapsed.as_millis());

        let now = std::time::SystemTime::now();
        t.reset(None);
        while !t.is_timed_out() {}
        let elapsed = std::time::SystemTime::now().duration_since(now).unwrap();
        eprintln!("Time elapsed #3: {} ms", elapsed.as_millis());

        eprintln!("{}", t);

@leiless
Copy link
Author

leiless commented May 17, 2023

fn estimate_ticks_per_second() {
    const N_THREAD: usize = 12;

    let mut handles = Vec::with_capacity(N_THREAD);
    let sleep_time = std::time::Duration::from_secs(1);

    for _i in 0..N_THREAD {
        let sleep_time_1 = sleep_time.clone();

        let h = std::thread::spawn(move || {
            let a = unsafe { core::arch::x86_64::_rdtsc() };
            std::thread::sleep(sleep_time_1);
            let b = unsafe { core::arch::x86_64::_rdtsc() };

            assert!(b >= a);

            b - a
        });
        handles.push(h);
    }

    let mut v = Vec::with_capacity(handles.len());

    for h in handles {
        let ticks_per_second = h.join().unwrap();
        v.push(ticks_per_second);
    }

    let min_tps = *v.iter().min().unwrap();
    let max_tps = *v.iter().max().unwrap();
    let avg_tps: u64 = v.iter().sum::<u64>() / N_THREAD as u64;
    let std_dev = std_deviation(v.as_slice());

    eprintln!(" N_THREAD: {}", N_THREAD);
    eprintln!("  Min TPS: {}", min_tps);
    eprintln!("  Max TPS: {}", max_tps);
    eprintln!("Max - Min: {}", max_tps - min_tps);
    eprintln!("  Avg TPS: {}", avg_tps);
    eprintln!("  Std dev: {}", std_dev.unwrap());
}

fn mean(data: &[u64]) -> Option<f64> {
    let sum = data.iter().sum::<u64>() as f64;
    let count = data.len();

    match count {
        positive if positive > 0 => Some(sum / count as f64),
        _ => None,
    }
}

// https://rust-lang-nursery.github.io/rust-cookbook/science/mathematics/statistics.html
fn std_deviation(data: &[u64]) -> Option<f64> {
    match (mean(data), data.len()) {
        (Some(data_mean), count) if count > 0 => {
            let variance = data.iter().map(|value| {
                let diff = data_mean - (*value as f64);

                diff * diff
            }).sum::<f64>() / count as f64;

            Some(variance.sqrt())
        }
        _ => None
    }
}
 N_THREAD: 12
  Min TPS: 3748208824
  Max TPS: 3754740006
Max - Min: 6531182
  Avg TPS: 3751327835
  Std dev: 2131743.207178706

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment