diff --git a/site/frontend/src/pages/detailed-query/page.vue b/site/frontend/src/pages/detailed-query/page.vue index 904417a97..efd496920 100644 --- a/site/frontend/src/pages/detailed-query/page.vue +++ b/site/frontend/src/pages/detailed-query/page.vue @@ -12,6 +12,7 @@ import { createTableData, createArtifactData, DeltaData, + TableRowData, } from "./utils"; const loading = ref(true); @@ -21,9 +22,10 @@ const showIncr = ref(true); const showDelta = ref(true); type SortDirection = "asc" | "desc"; +type ColumnName = keyof TableRowData; // Client-side sorting state -const currentSortColumn = ref("timeSeconds"); +const currentSortColumn = ref("timeSeconds"); const currentSortDirection = ref("desc"); // Computed properties for UI data @@ -75,6 +77,15 @@ const tableData = computed(() => { ? Math.abs(b.executionsDelta.percentage) : 0; break; + case "cacheHits": // Hits + aValue = a.cacheHits; + bValue = b.cacheHits; + // Use percentage change as secondary sort for equal absolute values + aSecondary = + a.cacheHitsDelta !== null ? Math.abs(a.cacheHitsDelta.percentage) : 0; + bSecondary = + b.cacheHitsDelta !== null ? Math.abs(b.cacheHitsDelta.percentage) : 0; + break; case "incrementalLoading": // Incremental loading (s) aValue = a.incrementalLoading; bValue = b.incrementalLoading; @@ -116,6 +127,15 @@ const tableData = computed(() => { ? Math.abs(b.executionsDelta.percentage) : 0; break; + case "cacheHitsDelta": // Cache hits delta + aValue = a.cacheHitsDelta !== null ? a.cacheHitsDelta.delta : -Infinity; + bValue = b.cacheHitsDelta !== null ? b.cacheHitsDelta.delta : -Infinity; + // Use percentage as secondary sort for equal delta values + aSecondary = + a.cacheHitsDelta !== null ? Math.abs(a.cacheHitsDelta.percentage) : 0; + bSecondary = + b.cacheHitsDelta !== null ? Math.abs(b.cacheHitsDelta.percentage) : 0; + break; case "incrementalLoadingDelta": // Incremental loading delta aValue = a.incrementalLoadingDelta !== null @@ -173,10 +193,10 @@ function loadSortFromUrl(urlParams: Dict) { const sort = urlParams["sort"] ?? "-timeSeconds"; // Default to descending timeSeconds // Handle sort format: either "columnName" for asc or "-columnName" for desc if (sort.startsWith("-")) { - currentSortColumn.value = sort.substring(1); + currentSortColumn.value = sort.substring(1) as ColumnName; currentSortDirection.value = "desc"; } else { - currentSortColumn.value = sort; + currentSortColumn.value = sort as ColumnName; currentSortDirection.value = "asc"; } } @@ -203,6 +223,7 @@ async function loadData() { base_commit: base_commit ?? null, benchmark, scenario, + sort_idx: "-2", }; selector.value = currentSelector; @@ -223,7 +244,7 @@ function populateUIData(responseData: SelfProfileResponse, state: Selector) { } function changeSortParameters( - columnName: string, + columnName: ColumnName, defaultDirection: SortDirection ) { // Toggle direction if clicking the same column, otherwise use default direction @@ -239,7 +260,7 @@ function changeSortParameters( storeSortToUrl(); } -function getHeaderClass(columnName: string): string { +function getHeaderClass(columnName: keyof TableRowData): string { if (columnName === currentSortColumn.value) { if (currentSortDirection.value === "asc") { return "header-sort-asc"; @@ -439,6 +460,20 @@ loadData(); >Executions delta + + Hits + + + Hits delta + Incremental loading (s)Incr. loading (s) Incremental loading deltaIncr. loading delta @@ -484,6 +519,10 @@ loadData(); + {{ row.cacheHits }} + + + {{ row.incrementalLoading.toFixed(3) }} diff --git a/site/frontend/src/pages/detailed-query/utils.ts b/site/frontend/src/pages/detailed-query/utils.ts index e8de53195..4d8da1542 100644 --- a/site/frontend/src/pages/detailed-query/utils.ts +++ b/site/frontend/src/pages/detailed-query/utils.ts @@ -14,10 +14,17 @@ export interface ProfileElement { label: string; self_time: number; percent_total_time: number; - number_of_cache_misses?: number; - number_of_cache_hits?: number; + number_of_cache_misses: number; + number_of_cache_hits: number; invocation_count: number; - blocked_time?: number; + blocked_time: number; + incremental_load_time: number; +} + +export interface ProfileElementDelta { + self_time: number; + invocation_count: number; + number_of_cache_hits: number; incremental_load_time: number; } @@ -34,7 +41,13 @@ export interface ProfileData { export interface SelfProfileResponse { profile: ProfileData; - base_profile_delta?: ProfileData; + base_profile_delta?: ProfileDataDelta; +} + +export interface ProfileDataDelta { + totals: ProfileElementDelta; + query_data: ProfileElementDelta[]; + artifact_sizes: ArtifactSize[]; } export function toSeconds(time: number): number { @@ -221,7 +234,7 @@ export interface DeltaData { isIntegral: boolean; } -interface TableRowData { +export interface TableRowData { isTotal: boolean; label: string; timePercent: {value: number; formatted: string; title: string}; @@ -229,6 +242,8 @@ interface TableRowData { timeDelta: DeltaData | null; executions: number; executionsDelta: DeltaData | null; + cacheHits: number; + cacheHitsDelta: DeltaData | null; incrementalLoading: number; incrementalLoadingDelta: DeltaData | null; } @@ -244,85 +259,66 @@ export function createTableData( // Add totals row first const totals = profile.totals; const totalsDelta = delta?.totals; - result.push({ - isTotal: true, - label: totals.label, + result.push(createRowData(true, totals, totalsDelta)); + + // Add query data rows + profile.query_data.forEach((query, idx) => { + const queryDelta = delta?.query_data[idx]; + result.push(createRowData(false, query, queryDelta)); + }); + + return result; +} + +function createRowData( + isTotal: boolean, + value: ProfileElement, + delta: ProfileElementDelta | undefined +): TableRowData { + return { + isTotal, + label: value.label, timePercent: - totals.percent_total_time < 0 + value.percent_total_time < 0 ? { value: -1, formatted: "-", title: "No wall-time stat collected for this run", } : { - value: totals.percent_total_time, - formatted: totals.percent_total_time.toFixed(2) + "%*", + value: value.percent_total_time, + formatted: value.percent_total_time.toFixed(2) + "%", title: "% of cpu-time stat", }, - timeSeconds: toSeconds(totals.self_time), - timeDelta: totalsDelta + timeSeconds: toSeconds(value.self_time), + timeDelta: delta ? createDelta( - toSeconds(totals.self_time), - toSeconds(totalsDelta.self_time), + toSeconds(value.self_time), + toSeconds(delta.self_time), false ) : null, - executions: totals.invocation_count, - executionsDelta: totalsDelta - ? createDelta(totals.invocation_count, totalsDelta.invocation_count, true) + executions: value.invocation_count, + executionsDelta: delta + ? createDelta(value.invocation_count, delta.invocation_count, true) + : null, + cacheHits: value.number_of_cache_hits, + cacheHitsDelta: delta + ? createDelta( + value.number_of_cache_hits, + delta.number_of_cache_hits, + true + ) : null, - incrementalLoading: toSeconds(totals.incremental_load_time), - incrementalLoadingDelta: totalsDelta + incrementalLoading: toSeconds(value.incremental_load_time), + incrementalLoadingDelta: delta ? createDelta( - toSeconds(totals.incremental_load_time), - toSeconds(totalsDelta.incremental_load_time), + toSeconds(value.incremental_load_time), + toSeconds(delta.incremental_load_time), false ) : null, - }); - - // Add query data rows - profile.query_data.forEach((query, idx) => { - const queryDelta = delta?.query_data[idx]; - result.push({ - isTotal: false, - label: query.label, - timePercent: - query.percent_total_time < 0 - ? { - value: -1, - formatted: "-", - title: "No wall-time stat collected for this run", - } - : { - value: query.percent_total_time, - formatted: query.percent_total_time.toFixed(2) + "%", - title: "", - }, - timeSeconds: toSeconds(query.self_time), - timeDelta: queryDelta - ? createDelta( - toSeconds(query.self_time), - toSeconds(queryDelta.self_time), - false - ) - : null, - executions: query.invocation_count, - executionsDelta: queryDelta - ? createDelta(query.invocation_count, queryDelta.invocation_count, true) - : null, - incrementalLoading: toSeconds(query.incremental_load_time), - incrementalLoadingDelta: queryDelta - ? createDelta( - toSeconds(query.incremental_load_time), - toSeconds(queryDelta.incremental_load_time), - false - ) - : null, - }); - }); - - return result; + }; } export function createArtifactData(data: SelfProfileResponse | null): { diff --git a/site/src/api.rs b/site/src/api.rs index ba3bc2be2..3b78d729d 100644 --- a/site/src/api.rs +++ b/site/src/api.rs @@ -504,6 +504,7 @@ pub mod self_profile { // Nanoseconds pub self_time: i64, pub invocation_count: i32, + pub number_of_cache_hits: i32, // Nanoseconds pub incremental_load_time: i64, } diff --git a/site/src/request_handlers/self_profile.rs b/site/src/request_handlers/self_profile.rs index db1707451..84c6bbd0f 100644 --- a/site/src/request_handlers/self_profile.rs +++ b/site/src/request_handlers/self_profile.rs @@ -214,6 +214,8 @@ fn get_self_profile_delta( self_time: profile.totals.self_time as i64 - base_profile.totals.self_time as i64, invocation_count: profile.totals.invocation_count as i32 - base_profile.totals.invocation_count as i32, + number_of_cache_hits: profile.totals.number_of_cache_hits as i32 + - base_profile.totals.number_of_cache_hits as i32, incremental_load_time: profile.totals.incremental_load_time as i64 - base_profile.totals.incremental_load_time as i64, }; @@ -230,6 +232,8 @@ fn get_self_profile_delta( let delta = self_profile::QueryDataDelta { self_time: qd.self_time as i64 - base_qd.self_time as i64, invocation_count: qd.invocation_count as i32 - base_qd.invocation_count as i32, + number_of_cache_hits: qd.number_of_cache_hits as i32 + - base_qd.number_of_cache_hits as i32, incremental_load_time: qd.incremental_load_time as i64 - base_qd.incremental_load_time as i64, }; @@ -240,6 +244,7 @@ fn get_self_profile_delta( let delta = self_profile::QueryDataDelta { self_time: qd.self_time as i64, invocation_count: qd.invocation_count as i32, + number_of_cache_hits: qd.number_of_cache_hits as i32, incremental_load_time: qd.incremental_load_time as i64, };