|
1 | 1 | use std::collections::HashSet;
|
2 | 2 | use std::io::Read;
|
3 | 3 | use std::sync::Arc;
|
4 |
| -use std::time::{Duration, Instant}; |
| 4 | +use std::time::Instant; |
5 | 5 |
|
6 | 6 | use bytes::Buf;
|
7 | 7 | use database::CommitType;
|
8 | 8 | use headers::{ContentType, Header};
|
9 | 9 | use hyper::StatusCode;
|
10 | 10 |
|
11 |
| -use crate::api::self_profile::{ArtifactSize, ArtifactSizeDelta}; |
| 11 | +use crate::api::self_profile::ArtifactSizeDelta; |
12 | 12 | use crate::api::{self_profile, self_profile_processed, self_profile_raw, ServerResult};
|
13 | 13 | use crate::comparison::Metric;
|
14 | 14 | use crate::db::ArtifactId;
|
15 | 15 | use crate::load::SiteCtxt;
|
16 | 16 | use crate::selector::{self};
|
17 | 17 | 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, |
19 | 20 | };
|
20 | 21 | use crate::server::{Response, ResponseHeaders};
|
21 | 22 |
|
@@ -154,85 +155,12 @@ pub async fn handle_self_profile_processed_download(
|
154 | 155 | builder.body(hyper::Body::from(output.data)).unwrap()
|
155 | 156 | }
|
156 | 157 |
|
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 |
| - |
230 | 158 | // Add query data entries to `profile` for any queries in `base_profile` which are not present in
|
231 | 159 | // `profile` (i.e. queries not invoked during the compilation that generated `profile`). This is
|
232 | 160 | // bit of a hack to enable showing rows for these queries in the table on the self-profile page.
|
233 | 161 | fn add_uninvoked_base_profile_queries(
|
234 | 162 | profile: &mut self_profile::SelfProfile,
|
235 |
| - base_profile: &Option<self_profile::SelfProfile>, |
| 163 | + base_profile: Option<&self_profile::SelfProfile>, |
236 | 164 | ) {
|
237 | 165 | let base_profile = match base_profile.as_ref() {
|
238 | 166 | Some(bp) => bp,
|
@@ -265,7 +193,7 @@ fn add_uninvoked_base_profile_queries(
|
265 | 193 |
|
266 | 194 | fn get_self_profile_delta(
|
267 | 195 | profile: &self_profile::SelfProfile,
|
268 |
| - base_profile: &Option<self_profile::SelfProfile>, |
| 196 | + base_profile: Option<&self_profile::SelfProfile>, |
269 | 197 | profiling_data: &analyzeme::AnalysisResults,
|
270 | 198 | base_profiling_data: Option<&analyzeme::AnalysisResults>,
|
271 | 199 | ) -> Option<self_profile::SelfProfileDelta> {
|
@@ -606,45 +534,72 @@ pub async fn handle_self_profile(
|
606 | 534 | assert_eq!(cpu_responses.len(), 1, "all selectors are exact");
|
607 | 535 | let mut cpu_response = cpu_responses.remove(0).series;
|
608 | 536 |
|
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 | + } |
623 | 562 | }
|
624 | 563 | }
|
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, |
635 | 587 | };
|
| 588 | + add_uninvoked_base_profile_queries( |
| 589 | + &mut self_profile.profile, |
| 590 | + base_self_profile.as_ref().map(|p| &p.profile), |
| 591 | + ); |
636 | 592 |
|
637 |
| - add_uninvoked_base_profile_queries(&mut profile, &base_profile); |
638 | 593 | 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), |
643 | 598 | );
|
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); |
645 | 600 |
|
646 | 601 | Ok(self_profile::Response {
|
647 | 602 | base_profile_delta,
|
648 |
| - profile, |
| 603 | + profile: self_profile.profile, |
649 | 604 | })
|
650 | 605 | }
|
0 commit comments