Skip to content

Commit 40fb983

Browse files
authored
Merge pull request #1747 from Kobzol/self-profile-cache
Add a cache for self profiles
2 parents ea6ff1a + 779deae commit 40fb983

File tree

5 files changed

+248
-109
lines changed

5 files changed

+248
-109
lines changed

Cargo.lock

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ uuid = { version = "1.3.0", features = ["v4"] }
4848
tera = "1.18"
4949
rust-embed = { version = "6.6.0", features = ["include-exclude", "interpolate-folder-path"] }
5050
humansize = "2"
51+
lru = "0.12.0"
5152

5253
[target.'cfg(unix)'.dependencies]
5354
jemallocator = "0.5"

site/src/load.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ use arc_swap::{ArcSwap, Guard};
88
use chrono::{Duration, Utc};
99
use lazy_static::lazy_static;
1010
use log::error;
11+
use parking_lot::Mutex;
1112
use regex::Regex;
1213
use serde::{Deserialize, Serialize};
1314

1415
use crate::db;
16+
use crate::self_profile::SelfProfileCache;
1517
use collector::compile::benchmark::category::Category;
1618
use collector::{Bound, MasterCommit};
1719
use database::Pool;
@@ -116,6 +118,9 @@ impl MasterCommitCache {
116118
}
117119
}
118120

121+
// How many analyzed self profiles should be stored in memory
122+
const CACHED_SELF_PROFILE_COUNT: usize = 1000;
123+
119124
/// Site context object that contains global data
120125
pub struct SiteCtxt {
121126
/// Site configuration
@@ -126,6 +131,8 @@ pub struct SiteCtxt {
126131
pub index: ArcSwap<crate::db::Index>,
127132
/// Cached master-branch Rust commits
128133
pub master_commits: Arc<ArcSwap<MasterCommitCache>>, // outer Arc enables mutation in background task
134+
/// Cache for self profile data
135+
pub self_profile_cache: Mutex<SelfProfileCache>,
129136
/// Database connection pool
130137
pub pool: Pool,
131138
}
@@ -174,6 +181,7 @@ impl SiteCtxt {
174181
master_commits: Arc::new(ArcSwap::new(Arc::new(master_commits))),
175182
pool,
176183
landing_page: ArcSwap::new(Arc::new(None)),
184+
self_profile_cache: Mutex::new(SelfProfileCache::new(CACHED_SELF_PROFILE_COUNT)),
177185
})
178186
}
179187

site/src/request_handlers/self_profile.rs

Lines changed: 64 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
use std::collections::HashSet;
22
use std::io::Read;
33
use std::sync::Arc;
4-
use std::time::{Duration, Instant};
4+
use std::time::Instant;
55

66
use bytes::Buf;
77
use database::CommitType;
88
use headers::{ContentType, Header};
99
use hyper::StatusCode;
1010

11-
use crate::api::self_profile::{ArtifactSize, ArtifactSizeDelta};
11+
use crate::api::self_profile::ArtifactSizeDelta;
1212
use crate::api::{self_profile, self_profile_processed, self_profile_raw, ServerResult};
1313
use crate::comparison::Metric;
1414
use crate::db::ArtifactId;
1515
use crate::load::SiteCtxt;
1616
use crate::selector::{self};
1717
use crate::self_profile::{
18-
extract_profiling_data, fetch_raw_self_profile_data, get_self_profile_raw_data,
18+
download_and_analyze_self_profile, get_self_profile_raw_data, SelfProfileKey,
19+
SelfProfileWithAnalysis,
1920
};
2021
use crate::server::{Response, ResponseHeaders};
2122

@@ -154,85 +155,12 @@ pub async fn handle_self_profile_processed_download(
154155
builder.body(hyper::Body::from(output.data)).unwrap()
155156
}
156157

157-
fn get_self_profile_data(
158-
cpu_clock: Option<f64>,
159-
profile: &analyzeme::AnalysisResults,
160-
) -> ServerResult<self_profile::SelfProfile> {
161-
let total_time: Duration = profile.query_data.iter().map(|qd| qd.self_time).sum();
162-
163-
let query_data = profile
164-
.query_data
165-
.iter()
166-
.map(|qd| self_profile::QueryData {
167-
label: qd.label.as_str().into(),
168-
self_time: qd.self_time.as_nanos() as u64,
169-
percent_total_time: ((qd.self_time.as_secs_f64() / total_time.as_secs_f64()) * 100.0)
170-
as f32,
171-
number_of_cache_misses: qd.number_of_cache_misses as u32,
172-
number_of_cache_hits: qd.number_of_cache_hits as u32,
173-
invocation_count: qd.invocation_count as u32,
174-
blocked_time: qd.blocked_time.as_nanos() as u64,
175-
incremental_load_time: qd.incremental_load_time.as_nanos() as u64,
176-
})
177-
.collect();
178-
179-
let totals = self_profile::QueryData {
180-
label: "Totals".into(),
181-
self_time: total_time.as_nanos() as u64,
182-
// TODO: check against wall-time from perf stats
183-
percent_total_time: cpu_clock
184-
.map(|w| ((total_time.as_secs_f64() / w) * 100.0) as f32)
185-
// sentinel "we couldn't compute this time"
186-
.unwrap_or(-100.0),
187-
number_of_cache_misses: profile
188-
.query_data
189-
.iter()
190-
.map(|qd| qd.number_of_cache_misses as u32)
191-
.sum(),
192-
number_of_cache_hits: profile
193-
.query_data
194-
.iter()
195-
.map(|qd| qd.number_of_cache_hits as u32)
196-
.sum(),
197-
invocation_count: profile
198-
.query_data
199-
.iter()
200-
.map(|qd| qd.invocation_count as u32)
201-
.sum(),
202-
blocked_time: profile
203-
.query_data
204-
.iter()
205-
.map(|qd| qd.blocked_time.as_nanos() as u64)
206-
.sum(),
207-
incremental_load_time: profile
208-
.query_data
209-
.iter()
210-
.map(|qd| qd.incremental_load_time.as_nanos() as u64)
211-
.sum(),
212-
};
213-
214-
let artifact_sizes = profile
215-
.artifact_sizes
216-
.iter()
217-
.map(|a| ArtifactSize {
218-
label: a.label.as_str().into(),
219-
bytes: a.value,
220-
})
221-
.collect();
222-
223-
Ok(self_profile::SelfProfile {
224-
query_data,
225-
totals,
226-
artifact_sizes: Some(artifact_sizes),
227-
})
228-
}
229-
230158
// Add query data entries to `profile` for any queries in `base_profile` which are not present in
231159
// `profile` (i.e. queries not invoked during the compilation that generated `profile`). This is
232160
// bit of a hack to enable showing rows for these queries in the table on the self-profile page.
233161
fn add_uninvoked_base_profile_queries(
234162
profile: &mut self_profile::SelfProfile,
235-
base_profile: &Option<self_profile::SelfProfile>,
163+
base_profile: Option<&self_profile::SelfProfile>,
236164
) {
237165
let base_profile = match base_profile.as_ref() {
238166
Some(bp) => bp,
@@ -265,7 +193,7 @@ fn add_uninvoked_base_profile_queries(
265193

266194
fn get_self_profile_delta(
267195
profile: &self_profile::SelfProfile,
268-
base_profile: &Option<self_profile::SelfProfile>,
196+
base_profile: Option<&self_profile::SelfProfile>,
269197
profiling_data: &analyzeme::AnalysisResults,
270198
base_profiling_data: Option<&analyzeme::AnalysisResults>,
271199
) -> Option<self_profile::SelfProfileDelta> {
@@ -606,45 +534,72 @@ pub async fn handle_self_profile(
606534
assert_eq!(cpu_responses.len(), 1, "all selectors are exact");
607535
let mut cpu_response = cpu_responses.remove(0).series;
608536

609-
let mut self_profile_data = Vec::new();
610-
let conn = ctxt.conn().await;
611-
for commit in commits.iter() {
612-
let aids_and_cids = conn
613-
.list_self_profile(commit.clone(), bench_name, profile, &body.scenario)
614-
.await;
615-
if let Some((aid, cid)) = aids_and_cids.first() {
616-
match fetch_raw_self_profile_data(*aid, bench_name, profile, scenario, *cid).await {
617-
Ok(d) => self_profile_data.push(
618-
extract_profiling_data(d)
619-
.map_err(|e| format!("error extracting self profiling data: {}", e))?,
620-
),
621-
Err(e) => return Err(format!("could not fetch raw profile data: {e:?}")),
622-
};
537+
async fn get_from_cache(
538+
ctxt: &SiteCtxt,
539+
aid: ArtifactId,
540+
benchmark: &str,
541+
profile: &str,
542+
scenario: database::Scenario,
543+
metric: Option<f64>,
544+
) -> ServerResult<SelfProfileWithAnalysis> {
545+
let key = SelfProfileKey {
546+
aid: aid.clone(),
547+
benchmark: benchmark.to_string(),
548+
profile: profile.to_string(),
549+
scenario,
550+
};
551+
let cache_result = ctxt.self_profile_cache.lock().get(&key);
552+
match cache_result {
553+
Some(res) => Ok(res),
554+
None => {
555+
let profile = download_and_analyze_self_profile(
556+
ctxt, aid, benchmark, profile, scenario, metric,
557+
)
558+
.await?;
559+
ctxt.self_profile_cache.lock().insert(key, profile.clone());
560+
Ok(profile)
561+
}
623562
}
624563
}
625-
let profiling_data = self_profile_data.remove(0).perform_analysis();
626-
let mut profile = get_self_profile_data(cpu_response.next().unwrap().1, &profiling_data)
627-
.map_err(|e| format!("{}: {}", body.commit, e))?;
628-
let (base_profile, base_raw_data) = if body.base_commit.is_some() {
629-
let base_profiling_data = self_profile_data.remove(0).perform_analysis();
630-
let profile = get_self_profile_data(cpu_response.next().unwrap().1, &base_profiling_data)
631-
.map_err(|e| format!("{}: {}", body.base_commit.as_ref().unwrap(), e))?;
632-
(Some(profile), Some(base_profiling_data))
633-
} else {
634-
(None, None)
564+
565+
let mut self_profile = get_from_cache(
566+
ctxt,
567+
commits.get(0).unwrap().clone(),
568+
bench_name,
569+
profile,
570+
scenario,
571+
cpu_response.next().unwrap().1,
572+
)
573+
.await?;
574+
let base_self_profile = match commits.get(1) {
575+
Some(aid) => Some(
576+
get_from_cache(
577+
ctxt,
578+
aid.clone(),
579+
bench_name,
580+
profile,
581+
scenario,
582+
cpu_response.next().unwrap().1,
583+
)
584+
.await?,
585+
),
586+
None => None,
635587
};
588+
add_uninvoked_base_profile_queries(
589+
&mut self_profile.profile,
590+
base_self_profile.as_ref().map(|p| &p.profile),
591+
);
636592

637-
add_uninvoked_base_profile_queries(&mut profile, &base_profile);
638593
let mut base_profile_delta = get_self_profile_delta(
639-
&profile,
640-
&base_profile,
641-
&profiling_data,
642-
base_raw_data.as_ref(),
594+
&self_profile.profile,
595+
base_self_profile.as_ref().map(|p| &p.profile),
596+
&self_profile.profiling_data,
597+
base_self_profile.as_ref().map(|p| &p.profiling_data),
643598
);
644-
sort_self_profile(&mut profile, &mut base_profile_delta, sort_idx);
599+
sort_self_profile(&mut self_profile.profile, &mut base_profile_delta, sort_idx);
645600

646601
Ok(self_profile::Response {
647602
base_profile_delta,
648-
profile,
603+
profile: self_profile.profile,
649604
})
650605
}

0 commit comments

Comments
 (0)