Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: larksuite/perf-monitor-rs
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: workflow-rs/workflow-perf-monitor-rs
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Able to merge. These branches can be automatically merged.
  • 9 commits
  • 9 files changed
  • 2 contributors

Commits on Aug 1, 2023

  1. "Refactor benchmark tests for CPU usage and update dependencies"

    Implemented changes to the benchmark tests in src/cpu/ios_macos.rs and transfered them to a new file named ios_macos.rs under the benchmarks/cpu directory. This helps in separating the CPU usage tests from the program file and adhere to the single-responsibility principle. Furthermore, added new dependency 'criterion' in Cargo.toml for benchmarking along with a corresponding bench setup. The removed lines in lib.rs is to allow these modifications, eliminating unnecessary features and dependencies.
    biryukovmaxim committed Aug 1, 2023
    Copy the full SHA
    e251aa9 View commit details
  2. "Expose ThreadId and get_thread_basic_info, enable benchmark testing …

    …for cpu_ios_macos.
    
    ThreadId structure and get_thread_basic_info function in src/cpu/ios_macos.rs file are now publicly accessible, allowing external code to use them. The `pub` keyword was added to the struct and function declarations to make them public. Benchmark test for cpu_ios_macos has been activated in Cargo.toml file by uncommenting respective lines. Update also includes adding dependencies and improving codes in benches/cpu/ios_macos.rs. This commit will support more extensive testing and functionality usage in iOS and MacOS platforms."
    biryukovmaxim committed Aug 1, 2023
    Copy the full SHA
    90f6a81 View commit details

Commits on Aug 2, 2023

  1. Add function to convert page count to bytes on Linux & Android

    This commit adds a function that determines the page size on Linux and Android systems and uses this to convert virtual memory size and resident set size from pages to bytes. This was necessary because previously the reported values were in pages, which could lead to confusion or incorrect information when being used elsewhere in the code. By doing the conversion in this function, we ensure that the values are always correctly represented in bytes.
    biryukovmaxim committed Aug 2, 2023
    Copy the full SHA
    20a4f7c View commit details
  2. Merge pull request #1 from biryukovmaxim/migration-to-criterion

    Migration to criterion
    aspect authored Aug 2, 2023
    Copy the full SHA
    6869a92 View commit details
  3. Merge pull request #2 from biryukovmaxim/change_linux_mem_stats_to_bytes

    Add function to convert page count to bytes on Linux & Android
    aspect authored Aug 2, 2023
    Copy the full SHA
    206a7d8 View commit details
  4. Copy the full SHA
    4795658 View commit details

Commits on Sep 17, 2023

  1. remove doc attributes

    aspect committed Sep 17, 2023
    Copy the full SHA
    4bfef90 View commit details
  2. code formatting

    aspect committed Sep 17, 2023
    Copy the full SHA
    66ce611 View commit details
  3. 0.0.2

    aspect committed Sep 17, 2023
    Copy the full SHA
    bbc4ecc View commit details
Showing with 116 additions and 68 deletions.
  1. +10 −2 Cargo.toml
  2. +4 −0 README.md
  3. +65 −0 benches/cpu/ios_macos.rs
  4. +6 −6 examples/activity_monitor.rs
  5. +2 −47 src/cpu/ios_macos.rs
  6. +3 −0 src/cpu/mod.rs
  7. +0 −5 src/lib.rs
  8. +0 −1 src/mem/mod.rs
  9. +26 −7 src/mem/process_memory_info.rs
12 changes: 10 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "perf_monitor"
version = "0.2.0"
name = "workflow-perf-monitor"
version = "0.0.2"
authors = ["zhangli.pear <zhangli.pear@bytedance.com>"]
edition = "2018"

@@ -31,3 +31,11 @@ mach = "0.3"
[build-dependencies]
bindgen = "0.59"
cc = "1.0"

[[bench]]
name = "cpu_ios_macos"
path = "benches/cpu/ios_macos.rs"
harness = false

[dev-dependencies]
criterion = "0.5.1"
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -6,6 +6,10 @@
[![docs.rs](https://docs.rs/perf_monitor/badge.svg)](https://docs.rs/perf_monitor)
[![crates.io](https://img.shields.io/crates/v/perf_monitor.svg)](https://crates.io/crates/perf_monitor)


This crate is an updated fork of https://github.com/larksuite/perf-monitor-rs used by projects based on the `workflow-rs`.


```toml
# Cargo.toml
[dependencies]
65 changes: 65 additions & 0 deletions benches/cpu/ios_macos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#[cfg(any(target_os = "ios", target_os = "macos"))]
mod tests {
use criterion::Criterion;
use libc::time_value_t;
use std::{convert::TryInto, time::Duration, time::Instant};
use workflow_perf_monitor::{cpu::get_thread_basic_info, cpu::ThreadId};

#[inline]
fn time_value_to_u64(t: time_value_t) -> u64 {
(t.seconds.try_into().unwrap_or(0u64))
.saturating_mul(1_000_000)
.saturating_add(t.microseconds.try_into().unwrap_or(0u64))
}
// There is a field named `cpu_usage` in `thread_basic_info` which represents the CPU usage of the thread.
// However, we have no idea about how long the interval is. And it will make the API being different from other platforms.
// We calculate the usage instead of using the field directory to make the API is the same on all platforms.
// The cost of the calculation is very very small according to the result of the following benchmark.
pub fn bench_cpu_usage_by_calculate(c: &mut Criterion) {
c.bench_function("CPU usage by calculate", |b| {
let tid = ThreadId::current();
let last_stat = get_thread_basic_info(tid).unwrap();
let last_time = Instant::now();
b.iter(|| {
let cur_stat = get_thread_basic_info(tid).unwrap();
let cur_time = Instant::now();

let cur_user_time = time_value_to_u64(cur_stat.user_time);
let cur_sys_time = time_value_to_u64(cur_stat.system_time);
let last_user_time = time_value_to_u64(last_stat.user_time);
let last_sys_time = time_value_to_u64(last_stat.system_time);

let dt_duration = cur_time - last_time;
let cpu_time_us = cur_user_time + cur_sys_time - last_user_time - last_sys_time;
let dt_wtime = Duration::from_micros(cpu_time_us);

let _ = (cur_stat, cur_time);
let _ = dt_wtime.as_micros() as f64 / dt_duration.as_micros() as f64;
});
});
}

pub fn bench_cpu_usage_by_field(c: &mut Criterion) {
c.bench_function("CPU usage by field", |b| {
let tid = ThreadId::current();
b.iter(|| {
let cur_stat = get_thread_basic_info(tid).unwrap();
let _ = cur_stat.cpu_usage / 1000;
});
});
}
}

#[cfg(any(target_os = "ios", target_os = "macos"))]
criterion::criterion_group!(
benches,
tests::bench_cpu_usage_by_calculate,
tests::bench_cpu_usage_by_field
);
#[cfg(any(target_os = "ios", target_os = "macos"))]
criterion::criterion_main!(benches);

#[cfg(not(any(target_os = "ios", target_os = "macos")))]
fn main() {
println!("This benchmark can only be run on iOS or MacOS.");
}
12 changes: 6 additions & 6 deletions examples/activity_monitor.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::time::Duration;
use std::time::Instant;

use perf_monitor::cpu::processor_numbers;
use perf_monitor::cpu::ProcessStat;
use perf_monitor::cpu::ThreadStat;
use perf_monitor::fd::fd_count_cur;
use perf_monitor::io::get_process_io_stats;
use perf_monitor::mem::get_process_memory_info;
use workflow_perf_monitor::cpu::processor_numbers;
use workflow_perf_monitor::cpu::ProcessStat;
use workflow_perf_monitor::cpu::ThreadStat;
use workflow_perf_monitor::fd::fd_count_cur;
use workflow_perf_monitor::io::get_process_io_stats;
use workflow_perf_monitor::mem::get_process_memory_info;

fn main() {
build_some_threads();
49 changes: 2 additions & 47 deletions src/cpu/ios_macos.rs
Original file line number Diff line number Diff line change
@@ -11,16 +11,15 @@ use std::{
};

#[derive(Clone, Copy)]
pub struct ThreadId(u32);
pub struct ThreadId(pub u32);

impl ThreadId {
#[inline]
pub fn current() -> Self {
ThreadId(unsafe { mach_thread_self() })
}
}

fn get_thread_basic_info(ThreadId(tid): ThreadId) -> Result<thread_basic_info> {
pub fn get_thread_basic_info(ThreadId(tid): ThreadId) -> Result<thread_basic_info> {
let mut thread_basic_info = MaybeUninit::<thread_basic_info>::uninit();
let mut thread_info_cnt = THREAD_BASIC_INFO_COUNT;

@@ -112,47 +111,3 @@ pub fn cpu_time() -> Result<Duration> {
.saturating_mul(1000);
Ok(Duration::new(sec, nsec))
}

#[cfg(test)]
#[allow(clippy::all, clippy::print_stdout)]
mod tests {
use super::*;
use test::Bencher;

// There is a field named `cpu_usage` in `thread_basic_info` which represents the CPU usage of the thread.
// However, we have no idea about how long the interval is. And it will make the API being different from other platforms.
// We calculate the usage instead of using the field directory to make the API is the same on all platforms.
// The cost of the calculation is very very small according to the result of the following benchmark.
#[bench]
fn bench_cpu_usage_by_calculate(b: &mut Bencher) {
let tid = ThreadId::current();
let last_stat = get_thread_basic_info(tid).unwrap();
let last_time = Instant::now();

b.iter(|| {
let cur_stat = get_thread_basic_info(tid).unwrap();
let cur_time = Instant::now();

let cur_user_time = time_value_to_u64(cur_stat.user_time);
let cur_sys_time = time_value_to_u64(cur_stat.system_time);
let last_user_time = time_value_to_u64(last_stat.user_time);
let last_sys_time = time_value_to_u64(last_stat.system_time);

let dt_duration = cur_time - last_time;
let cpu_time_us = cur_user_time + cur_sys_time - last_user_time - last_sys_time;
let dt_wtime = Duration::from_micros(cpu_time_us);

let _ = (cur_stat, cur_time);
let _ = dt_wtime.as_micros() as f64 / dt_duration.as_micros() as f64;
});
}

#[bench]
fn bench_cpu_usage_by_field(b: &mut Bencher) {
let tid = ThreadId::current();
b.iter(|| {
let cur_stat = get_thread_basic_info(tid).unwrap();
let _ = cur_stat.cpu_usage / 1000;
});
}
}
3 changes: 3 additions & 0 deletions src/cpu/mod.rs
Original file line number Diff line number Diff line change
@@ -45,8 +45,11 @@ use ios_macos as platform;
#[cfg(target_os = "windows")]
use windows as platform;

#[cfg(any(target_os = "ios", target_os = "macos"))]
pub use ios_macos::get_thread_basic_info;
pub use platform::{cpu_time, ThreadId};
pub use std::io::Result;

use std::{
io, mem,
time::{Duration, Instant},
5 changes: 0 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -3,11 +3,6 @@
//!
#![cfg_attr(test, allow(clippy::all, clippy::unwrap_used))]
#![cfg_attr(doc, feature(doc_cfg))]
#![cfg_attr(test, feature(test))]

#[cfg(test)]
extern crate test;

#[allow(warnings)]
#[cfg(any(target_os = "macos", target_os = "ios"))]
1 change: 0 additions & 1 deletion src/mem/mod.rs
Original file line number Diff line number Diff line change
@@ -17,5 +17,4 @@ mod process_memory_info;
pub use process_memory_info::{get_process_memory_info, ProcessMemoryInfo};

#[cfg(target_os = "macos")]
#[cfg_attr(doc, doc(cfg(macos)))]
pub mod apple;
33 changes: 26 additions & 7 deletions src/mem/process_memory_info.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@ pub struct ProcessMemoryInfo {
/// column of taskmgr.exe.
pub resident_set_size: u64,
#[cfg(not(any(target_os = "android", target_os = "linux")))]
#[cfg_attr(doc, doc(cfg(not(linux))))]
pub resident_set_size_peak: u64,

/// this is the total amount of virtual memory used by the process.
@@ -72,20 +71,40 @@ fn get_process_memory_info_impl() -> Result<ProcessMemoryInfo> {
})
}

#[cfg(any(target_os = "linux", target_os = "android"))]
#[inline]
fn page_size() -> u64 {
static INIT: std::sync::Once = std::sync::Once::new();
static mut PAGE_SIZE: u64 = 0;

unsafe {
INIT.call_once(|| PAGE_SIZE = libc::sysconf(libc::_SC_PAGESIZE) as u64);
PAGE_SIZE
}
}

#[cfg(any(target_os = "linux", target_os = "android"))]
fn get_process_memory_info_impl() -> Result<ProcessMemoryInfo> {
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt
let statm = std::fs::read_to_string("/proc/self/statm")?;
let mut parts = statm.split(' ');
let Some(virtual_memory_size) = parts.next().and_then(|s| s.parse().ok()) else {
return Err(Error::new(std::io::ErrorKind::Other, "Invalid VmSize in /proc/self/statm"));
let Some(virtual_memory_size_pages): Option<u64> = parts.next().and_then(|s| s.parse().ok())
else {
return Err(Error::new(
std::io::ErrorKind::Other,
"Invalid VmSize in /proc/self/statm",
));
};
let Some(resident_set_size) = parts.next().and_then(|s| s.parse().ok()) else {
return Err(Error::new(std::io::ErrorKind::Other, "Invalid VmRSS in /proc/self/statm"));
let Some(resident_set_size_pages): Option<u64> = parts.next().and_then(|s| s.parse().ok())
else {
return Err(Error::new(
std::io::ErrorKind::Other,
"Invalid VmRSS in /proc/self/statm",
));
};
Ok(ProcessMemoryInfo {
virtual_memory_size,
resident_set_size,
virtual_memory_size: virtual_memory_size_pages * page_size(),
resident_set_size: resident_set_size_pages * page_size(),
})
}