|
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