aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_prof/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_prof/src')
-rw-r--r--crates/ra_prof/src/lib.rs17
-rw-r--r--crates/ra_prof/src/memory_usage.rs52
-rw-r--r--crates/ra_prof/src/stop_watch.rs86
3 files changed, 128 insertions, 27 deletions
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 89df7f04b..eb50965ae 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -1,5 +1,6 @@
1//! A collection of tools for profiling rust-analyzer. 1//! A collection of tools for profiling rust-analyzer.
2 2
3mod stop_watch;
3mod memory_usage; 4mod memory_usage;
4#[cfg(feature = "cpu_profiler")] 5#[cfg(feature = "cpu_profiler")]
5mod google_cpu_profiler; 6mod google_cpu_profiler;
@@ -11,14 +12,9 @@ use std::cell::RefCell;
11pub use crate::{ 12pub use crate::{
12 hprof::{init, init_from, profile}, 13 hprof::{init, init_from, profile},
13 memory_usage::{Bytes, MemoryUsage}, 14 memory_usage::{Bytes, MemoryUsage},
15 stop_watch::{StopWatch, StopWatchSpan},
14}; 16};
15 17
16// We use jemalloc mainly to get heap usage statistics, actual performance
17// difference is not measures.
18#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
19#[global_allocator]
20static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
21
22/// Prints backtrace to stderr, useful for debugging. 18/// Prints backtrace to stderr, useful for debugging.
23#[cfg(feature = "backtrace")] 19#[cfg(feature = "backtrace")]
24pub fn print_backtrace() { 20pub fn print_backtrace() {
@@ -43,6 +39,7 @@ pub struct Scope {
43} 39}
44 40
45impl Scope { 41impl Scope {
42 #[must_use]
46 pub fn enter() -> Scope { 43 pub fn enter() -> Scope {
47 let prev = IN_SCOPE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), true)); 44 let prev = IN_SCOPE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), true));
48 Scope { prev } 45 Scope { prev }
@@ -65,7 +62,8 @@ impl Drop for Scope {
65/// 2. Build with `cpu_profiler` feature. 62/// 2. Build with `cpu_profiler` feature.
66/// 3. Tun the code, the *raw* output would be in the `./out.profile` file. 63/// 3. Tun the code, the *raw* output would be in the `./out.profile` file.
67/// 4. Install pprof for visualization (https://github.com/google/pprof). 64/// 4. Install pprof for visualization (https://github.com/google/pprof).
68/// 5. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. 65/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000`
66/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results.
69/// 67///
70/// For example, here's how I run profiling on NixOS: 68/// For example, here's how I run profiling on NixOS:
71/// 69///
@@ -73,11 +71,16 @@ impl Drop for Scope {
73/// $ nix-shell -p gperftools --run \ 71/// $ nix-shell -p gperftools --run \
74/// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null' 72/// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null'
75/// ``` 73/// ```
74///
75/// See this diff for how to profile completions:
76///
77/// https://github.com/rust-analyzer/rust-analyzer/pull/5306
76#[derive(Debug)] 78#[derive(Debug)]
77pub struct CpuProfiler { 79pub struct CpuProfiler {
78 _private: (), 80 _private: (),
79} 81}
80 82
83#[must_use]
81pub fn cpu_profiler() -> CpuProfiler { 84pub fn cpu_profiler() -> CpuProfiler {
82 #[cfg(feature = "cpu_profiler")] 85 #[cfg(feature = "cpu_profiler")]
83 { 86 {
diff --git a/crates/ra_prof/src/memory_usage.rs b/crates/ra_prof/src/memory_usage.rs
index 9768f656c..c2ecbd33c 100644
--- a/crates/ra_prof/src/memory_usage.rs
+++ b/crates/ra_prof/src/memory_usage.rs
@@ -1,46 +1,58 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2
3use std::fmt; 2use std::fmt;
4 3
4use cfg_if::cfg_if;
5
6#[derive(Copy, Clone)]
5pub struct MemoryUsage { 7pub struct MemoryUsage {
6 pub allocated: Bytes, 8 pub allocated: Bytes,
7 pub resident: Bytes,
8} 9}
9 10
10impl MemoryUsage { 11impl fmt::Display for MemoryUsage {
11 #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] 12 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
12 pub fn current() -> MemoryUsage { 13 write!(fmt, "{}", self.allocated)
13 jemalloc_ctl::epoch::advance().unwrap();
14 MemoryUsage {
15 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap()),
16 resident: Bytes(jemalloc_ctl::stats::resident::read().unwrap()),
17 }
18 } 14 }
15}
19 16
20 #[cfg(any(not(feature = "jemalloc"), target_env = "msvc"))] 17impl std::ops::Sub for MemoryUsage {
21 pub fn current() -> MemoryUsage { 18 type Output = MemoryUsage;
22 MemoryUsage { allocated: Bytes(0), resident: Bytes(0) } 19 fn sub(self, rhs: MemoryUsage) -> MemoryUsage {
20 MemoryUsage { allocated: self.allocated - rhs.allocated }
23 } 21 }
24} 22}
25 23
26impl fmt::Display for MemoryUsage { 24impl MemoryUsage {
27 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 25 pub fn current() -> MemoryUsage {
28 write!(fmt, "{} allocated {} resident", self.allocated, self.resident,) 26 cfg_if! {
27 if #[cfg(target_os = "linux")] {
28 // Note: This is incredibly slow.
29 let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
30 MemoryUsage { allocated: Bytes(alloc) }
31 } else {
32 MemoryUsage { allocated: Bytes(0) }
33 }
34 }
29 } 35 }
30} 36}
31 37
32#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] 38#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
33pub struct Bytes(usize); 39pub struct Bytes(isize);
40
41impl Bytes {
42 pub fn megabytes(self) -> isize {
43 self.0 / 1024 / 1024
44 }
45}
34 46
35impl fmt::Display for Bytes { 47impl fmt::Display for Bytes {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 let bytes = self.0; 49 let bytes = self.0;
38 let mut value = bytes; 50 let mut value = bytes;
39 let mut suffix = "b"; 51 let mut suffix = "b";
40 if value > 4096 { 52 if value.abs() > 4096 {
41 value /= 1024; 53 value /= 1024;
42 suffix = "kb"; 54 suffix = "kb";
43 if value > 4096 { 55 if value.abs() > 4096 {
44 value /= 1024; 56 value /= 1024;
45 suffix = "mb"; 57 suffix = "mb";
46 } 58 }
@@ -51,7 +63,7 @@ impl fmt::Display for Bytes {
51 63
52impl std::ops::AddAssign<usize> for Bytes { 64impl std::ops::AddAssign<usize> for Bytes {
53 fn add_assign(&mut self, x: usize) { 65 fn add_assign(&mut self, x: usize) {
54 self.0 += x; 66 self.0 += x as isize;
55 } 67 }
56} 68}
57 69
diff --git a/crates/ra_prof/src/stop_watch.rs b/crates/ra_prof/src/stop_watch.rs
new file mode 100644
index 000000000..5e276190e
--- /dev/null
+++ b/crates/ra_prof/src/stop_watch.rs
@@ -0,0 +1,86 @@
1//! Like `std::time::Instant`, but also measures memory & CPU cycles.
2use std::{
3 fmt,
4 time::{Duration, Instant},
5};
6
7use crate::MemoryUsage;
8
9pub struct StopWatch {
10 time: Instant,
11 #[cfg(target_os = "linux")]
12 counter: Option<perf_event::Counter>,
13 memory: Option<MemoryUsage>,
14}
15
16pub struct StopWatchSpan {
17 pub time: Duration,
18 pub instructions: Option<u64>,
19 pub memory: Option<MemoryUsage>,
20}
21
22impl StopWatch {
23 pub fn start() -> StopWatch {
24 #[cfg(target_os = "linux")]
25 let counter = {
26 let mut counter = perf_event::Builder::new()
27 .build()
28 .map_err(|err| eprintln!("Failed to create perf counter: {}", err))
29 .ok();
30 if let Some(counter) = &mut counter {
31 if let Err(err) = counter.enable() {
32 eprintln!("Failed to start perf counter: {}", err)
33 }
34 }
35 counter
36 };
37 let time = Instant::now();
38 StopWatch {
39 time,
40 #[cfg(target_os = "linux")]
41 counter,
42 memory: None,
43 }
44 }
45 pub fn memory(mut self, yes: bool) -> StopWatch {
46 if yes {
47 self.memory = Some(MemoryUsage::current());
48 }
49 self
50 }
51 pub fn elapsed(&mut self) -> StopWatchSpan {
52 let time = self.time.elapsed();
53
54 #[cfg(target_os = "linux")]
55 let instructions = self.counter.as_mut().and_then(|it| {
56 it.read().map_err(|err| eprintln!("Failed to read perf counter: {}", err)).ok()
57 });
58 #[cfg(not(target_os = "linux"))]
59 let instructions = None;
60
61 let memory = self.memory.map(|it| MemoryUsage::current() - it);
62 StopWatchSpan { time, instructions, memory }
63 }
64}
65
66impl fmt::Display for StopWatchSpan {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "{:.2?}", self.time)?;
69 if let Some(mut instructions) = self.instructions {
70 let mut prefix = "";
71 if instructions > 10000 {
72 instructions /= 1000;
73 prefix = "k"
74 }
75 if instructions > 10000 {
76 instructions /= 1000;
77 prefix = "m"
78 }
79 write!(f, ", {}{}i", instructions, prefix)?;
80 }
81 if let Some(memory) = self.memory {
82 write!(f, ", {}", memory)?;
83 }
84 Ok(())
85 }
86}