From 1efec8e0d0698958ac7b3253c1b0893b9acfabaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Wed, 16 Aug 2023 14:11:46 +0100 Subject: [PATCH] [Profiling] Diff topN functions impact estimate fix (#163749) Context: A few weeks ago I merged a PR that moved the calculation of the impact estimate to the server. But by doing so I missed an essential part to calculate it correct, the scale factor that is defined in the UI. I'm reverting that change and making the calcs in the client again. Along with that I fixed a problem and refactored the function that calculate the impact simplifying it. Before: Screenshot 2023-08-14 at 10 06 39 AM After: Screenshot 2023-08-14 at 10 07 29 AM Screenshot 2023-08-16 at 10 03 52 AM --- .../calculate_impact_estimates.test.ts | 86 +++++++++++------- .../calculate_impact_estimates/index.ts | 46 ++++++---- .../profiling/common/functions.test.ts | 3 +- x-pack/plugins/profiling/common/functions.ts | 44 ++-------- .../flamegraph/flamegraph_tooltip.tsx | 16 ++-- .../get_impact_rows.tsx | 45 ++++------ .../topn_functions/function_row.tsx | 12 +-- .../components/topn_functions/index.tsx | 9 +- .../components/topn_functions/utils.test.ts | 12 ++- .../public/components/topn_functions/utils.ts | 51 ++++++++--- .../topn_functions_summary/index.tsx | 88 +++++++++++++------ .../summary_item.test.ts | 21 +++++ .../topn_functions_summary/summary_item.tsx | 6 +- .../functions/differential_topn/index.tsx | 10 +-- .../profiling/server/routes/functions.ts | 1 - 15 files changed, 271 insertions(+), 179 deletions(-) create mode 100644 x-pack/plugins/profiling/public/components/topn_functions_summary/summary_item.test.ts diff --git a/x-pack/plugins/profiling/common/calculate_impact_estimates/calculate_impact_estimates.test.ts b/x-pack/plugins/profiling/common/calculate_impact_estimates/calculate_impact_estimates.test.ts index fda66eefe38894..626ec8a26dfc23 100644 --- a/x-pack/plugins/profiling/common/calculate_impact_estimates/calculate_impact_estimates.test.ts +++ b/x-pack/plugins/profiling/common/calculate_impact_estimates/calculate_impact_estimates.test.ts @@ -8,53 +8,79 @@ import { calculateImpactEstimates } from '.'; describe('calculateImpactEstimates', () => { it('calculates impact when countExclusive is lower than countInclusive', () => { - expect( - calculateImpactEstimates({ - countExclusive: 500, - countInclusive: 1000, - totalSamples: 10000, - totalSeconds: 15 * 60, // 15m - }) - ).toEqual({ + const { selfCPU, totalCPU, totalSamples } = calculateImpactEstimates({ + countExclusive: 500, + countInclusive: 1000, + totalSamples: 10000, + totalSeconds: 15 * 60, // 15m + }); + + expect(totalCPU).toEqual({ annualizedCo2: 17.909333333333336, - annualizedCo2NoChildren: 8.954666666666668, annualizedCoreSeconds: 1752000, - annualizedCoreSecondsNoChildren: 876000, annualizedDollarCost: 20.683333333333334, - annualizedDollarCostNoChildren: 10.341666666666667, co2: 0.0005111111111111112, - co2NoChildren: 0.0002555555555555556, coreSeconds: 50, - coreSecondsNoChildren: 25, dollarCost: 0.0005902777777777778, - dollarCostNoChildren: 0.0002951388888888889, percentage: 0.1, - percentageNoChildren: 0.05, + }); + + expect(selfCPU).toEqual({ + annualizedCo2: 8.954666666666668, + annualizedCoreSeconds: 876000, + annualizedDollarCost: 10.341666666666667, + co2: 0.0002555555555555556, + coreSeconds: 25, + dollarCost: 0.0002951388888888889, + percentage: 0.05, + }); + + expect(totalSamples).toEqual({ + percentage: 1, + coreSeconds: 500, + annualizedCoreSeconds: 17520000, + co2: 0.005111111111111111, + annualizedCo2: 179.09333333333333, + dollarCost: 0.0059027777777777785, + annualizedDollarCost: 206.83333333333337, }); }); it('calculates impact', () => { - expect( - calculateImpactEstimates({ - countExclusive: 1000, - countInclusive: 1000, - totalSamples: 10000, - totalSeconds: 15 * 60, // 15m - }) - ).toEqual({ + const { selfCPU, totalCPU, totalSamples } = calculateImpactEstimates({ + countExclusive: 1000, + countInclusive: 1000, + totalSamples: 10000, + totalSeconds: 15 * 60, // 15m + }); + + expect(totalCPU).toEqual({ annualizedCo2: 17.909333333333336, - annualizedCo2NoChildren: 17.909333333333336, annualizedCoreSeconds: 1752000, - annualizedCoreSecondsNoChildren: 1752000, annualizedDollarCost: 20.683333333333334, - annualizedDollarCostNoChildren: 20.683333333333334, co2: 0.0005111111111111112, - co2NoChildren: 0.0005111111111111112, coreSeconds: 50, - coreSecondsNoChildren: 50, dollarCost: 0.0005902777777777778, - dollarCostNoChildren: 0.0005902777777777778, percentage: 0.1, - percentageNoChildren: 0.1, + }); + + expect(selfCPU).toEqual({ + annualizedCo2: 17.909333333333336, + annualizedCoreSeconds: 1752000, + annualizedDollarCost: 20.683333333333334, + co2: 0.0005111111111111112, + coreSeconds: 50, + dollarCost: 0.0005902777777777778, + percentage: 0.1, + }); + + expect(totalSamples).toEqual({ + percentage: 1, + coreSeconds: 500, + annualizedCoreSeconds: 17520000, + co2: 0.005111111111111111, + annualizedCo2: 179.09333333333333, + dollarCost: 0.0059027777777777785, + annualizedDollarCost: 206.83333333333337, }); }); }); diff --git a/x-pack/plugins/profiling/common/calculate_impact_estimates/index.ts b/x-pack/plugins/profiling/common/calculate_impact_estimates/index.ts index e7799fd9acde16..70cfdc109a1077 100644 --- a/x-pack/plugins/profiling/common/calculate_impact_estimates/index.ts +++ b/x-pack/plugins/profiling/common/calculate_impact_estimates/index.ts @@ -27,40 +27,52 @@ export function calculateImpactEstimates({ totalSamples: number; totalSeconds: number; }) { - const annualizedScaleUp = ANNUAL_SECONDS / totalSeconds; + return { + totalSamples: calculateImpact({ + samples: totalSamples, + totalSamples, + totalSeconds, + }), + totalCPU: calculateImpact({ + samples: countInclusive, + totalSamples, + totalSeconds, + }), + selfCPU: calculateImpact({ + samples: countExclusive, + totalSamples, + totalSeconds, + }), + }; +} - const percentage = countInclusive / totalSamples; - const percentageNoChildren = countExclusive / totalSamples; +function calculateImpact({ + samples, + totalSamples, + totalSeconds, +}: { + samples: number; + totalSamples: number; + totalSeconds: number; +}) { + const annualizedScaleUp = ANNUAL_SECONDS / totalSeconds; const totalCoreSeconds = totalSamples / 20; + const percentage = samples / totalSamples; const coreSeconds = totalCoreSeconds * percentage; const annualizedCoreSeconds = coreSeconds * annualizedScaleUp; - const coreSecondsNoChildren = totalCoreSeconds * percentageNoChildren; - const annualizedCoreSecondsNoChildren = coreSecondsNoChildren * annualizedScaleUp; const coreHours = coreSeconds / (60 * 60); - const coreHoursNoChildren = coreSecondsNoChildren / (60 * 60); const co2 = ((PER_CORE_WATT * coreHours) / 1000.0) * CO2_PER_KWH; - const co2NoChildren = ((PER_CORE_WATT * coreHoursNoChildren) / 1000.0) * CO2_PER_KWH; const annualizedCo2 = co2 * annualizedScaleUp; - const annualizedCo2NoChildren = co2NoChildren * annualizedScaleUp; const dollarCost = coreHours * CORE_COST_PER_HOUR; const annualizedDollarCost = dollarCost * annualizedScaleUp; - const dollarCostNoChildren = coreHoursNoChildren * CORE_COST_PER_HOUR; - const annualizedDollarCostNoChildren = dollarCostNoChildren * annualizedScaleUp; return { percentage, - percentageNoChildren, coreSeconds, annualizedCoreSeconds, - coreSecondsNoChildren, - annualizedCoreSecondsNoChildren, co2, - co2NoChildren, annualizedCo2, - annualizedCo2NoChildren, dollarCost, annualizedDollarCost, - dollarCostNoChildren, - annualizedDollarCostNoChildren, }; } diff --git a/x-pack/plugins/profiling/common/functions.test.ts b/x-pack/plugins/profiling/common/functions.test.ts index 10578dab72b514..1ba31d397a3386 100644 --- a/x-pack/plugins/profiling/common/functions.test.ts +++ b/x-pack/plugins/profiling/common/functions.test.ts @@ -17,7 +17,7 @@ describe('TopN function operations', () => { const { events, stackTraces, stackFrames, executables, samplingRate } = decodeStackTraceResponse(response); - describe(`stacktraces from ${seconds} seconds and upsampled by ${upsampledBy}`, () => { + describe(`stacktraces upsampled by ${upsampledBy}`, () => { const maxTopN = 5; const topNFunctions = createTopNFunctions({ events, @@ -27,7 +27,6 @@ describe('TopN function operations', () => { startIndex: 0, endIndex: maxTopN, samplingRate, - totalSeconds: seconds, }); const exclusiveCounts = topNFunctions.TopN.map((value) => value.CountExclusive); diff --git a/x-pack/plugins/profiling/common/functions.ts b/x-pack/plugins/profiling/common/functions.ts index caff616d1951cd..304c56b81e906a 100644 --- a/x-pack/plugins/profiling/common/functions.ts +++ b/x-pack/plugins/profiling/common/functions.ts @@ -6,7 +6,6 @@ */ import * as t from 'io-ts'; import { sumBy } from 'lodash'; -import { calculateImpactEstimates } from './calculate_impact_estimates'; import { createFrameGroupID, FrameGroupID } from './frame_group'; import { createStackFrameMetadata, @@ -35,18 +34,14 @@ type TopNFunction = Pick< > & { Id: string; Rank: number; - impactEstimates?: ReturnType; - selfCPUPerc: number; - totalCPUPerc: number; }; export interface TopNFunctions { TotalCount: number; TopN: TopNFunction[]; SamplingRate: number; - impactEstimates?: ReturnType; - selfCPUPerc: number; - totalCPUPerc: number; + selfCPU: number; + totalCPU: number; } export function createTopNFunctions({ @@ -57,7 +52,6 @@ export function createTopNFunctions({ stackFrames, stackTraces, startIndex, - totalSeconds, }: { endIndex: number; events: Map; @@ -66,7 +60,6 @@ export function createTopNFunctions({ stackFrames: Map; stackTraces: Map; startIndex: number; - totalSeconds: number; }): TopNFunctions { // The `count` associated with a frame provides the total number of // traces in which that node has appeared at least once. However, a @@ -167,52 +160,25 @@ export function createTopNFunctions({ const framesAndCountsAndIds = topN.slice(startIndex, endIndex).map((frameAndCount, i) => { const countExclusive = frameAndCount.CountExclusive; const countInclusive = frameAndCount.CountInclusive; - const totalCPUPerc = (countInclusive / totalCount) * 100; - const selfCPUPerc = (countExclusive / totalCount) * 100; - - const impactEstimates = - totalSeconds > 0 - ? calculateImpactEstimates({ - countExclusive, - countInclusive, - totalSamples: totalCount, - totalSeconds, - }) - : undefined; + return { Rank: i + 1, Frame: frameAndCount.Frame, CountExclusive: countExclusive, - selfCPUPerc, CountInclusive: countInclusive, - totalCPUPerc, Id: frameAndCount.FrameGroupID, - impactEstimates, }; }); const sumSelfCPU = sumBy(framesAndCountsAndIds, 'CountExclusive'); - const selfCPUPerc = (sumSelfCPU / totalCount) * 100; const sumTotalCPU = sumBy(framesAndCountsAndIds, 'CountInclusive'); - const totalCPUPerc = (sumTotalCPU / totalCount) * 100; - - const impactEstimates = - totalSeconds > 0 - ? calculateImpactEstimates({ - countExclusive: sumSelfCPU, - countInclusive: sumTotalCPU, - totalSamples: totalCount, - totalSeconds, - }) - : undefined; return { TotalCount: totalCount, TopN: framesAndCountsAndIds, SamplingRate: samplingRate, - impactEstimates, - selfCPUPerc, - totalCPUPerc, + selfCPU: sumSelfCPU, + totalCPU: sumTotalCPU, }; } diff --git a/x-pack/plugins/profiling/public/components/flamegraph/flamegraph_tooltip.tsx b/x-pack/plugins/profiling/public/components/flamegraph/flamegraph_tooltip.tsx index 097997d970833c..ce5835f57e7a7a 100644 --- a/x-pack/plugins/profiling/public/components/flamegraph/flamegraph_tooltip.tsx +++ b/x-pack/plugins/profiling/public/components/flamegraph/flamegraph_tooltip.tsx @@ -99,8 +99,8 @@ export function FlameGraphTooltip({ labelStyle={{ fontWeight: 'bold' }} /> } - value={impactEstimates.percentage} - comparison={comparisonImpactEstimates?.percentage} + value={impactEstimates.totalCPU.percentage} + comparison={comparisonImpactEstimates?.totalCPU.percentage} formatValue={asPercentage} showDifference formatDifferenceAsPercentage @@ -115,8 +115,8 @@ export function FlameGraphTooltip({ labelStyle={{ fontWeight: 'bold' }} /> } - value={impactEstimates.percentageNoChildren} - comparison={comparisonImpactEstimates?.percentageNoChildren} + value={impactEstimates.selfCPU.percentage} + comparison={comparisonImpactEstimates?.selfCPU.percentage} showDifference formatDifferenceAsPercentage formatValue={asPercentage} @@ -144,8 +144,8 @@ export function FlameGraphTooltip({ label={i18n.translate('xpack.profiling.flameGraphTooltip.annualizedCo2', { defaultMessage: `Annualized CO2`, })} - value={impactEstimates.annualizedCo2} - comparison={comparisonImpactEstimates?.annualizedCo2} + value={impactEstimates.totalCPU.annualizedCo2} + comparison={comparisonImpactEstimates?.totalCPU.annualizedCo2} formatValue={asWeight} showDifference formatDifferenceAsPercentage={false} @@ -155,8 +155,8 @@ export function FlameGraphTooltip({ label={i18n.translate('xpack.profiling.flameGraphTooltip.annualizedDollarCost', { defaultMessage: `Annualized dollar cost`, })} - value={impactEstimates.annualizedDollarCost} - comparison={comparisonImpactEstimates?.annualizedDollarCost} + value={impactEstimates.totalCPU.annualizedDollarCost} + comparison={comparisonImpactEstimates?.totalCPU.annualizedDollarCost} formatValue={asCost} showDifference formatDifferenceAsPercentage={false} diff --git a/x-pack/plugins/profiling/public/components/frame_information_window/get_impact_rows.tsx b/x-pack/plugins/profiling/public/components/frame_information_window/get_impact_rows.tsx index f2ab508e10c581..43e34b6f6901ed 100644 --- a/x-pack/plugins/profiling/public/components/frame_information_window/get_impact_rows.tsx +++ b/x-pack/plugins/profiling/public/components/frame_information_window/get_impact_rows.tsx @@ -28,22 +28,7 @@ export function getImpactRows({ totalSeconds: number; isApproximate: boolean; }) { - const { - percentage, - percentageNoChildren, - coreSeconds, - annualizedCoreSeconds, - coreSecondsNoChildren, - co2, - co2NoChildren, - annualizedCo2, - annualizedCo2NoChildren, - dollarCost, - dollarCostNoChildren, - annualizedDollarCost, - annualizedDollarCostNoChildren, - annualizedCoreSecondsNoChildren, - } = calculateImpactEstimates({ + const { selfCPU, totalCPU } = calculateImpactEstimates({ countInclusive, countExclusive, totalSamples, @@ -53,11 +38,11 @@ export function getImpactRows({ const impactRows = [ { label: , - value: asPercentage(percentage), + value: asPercentage(totalCPU.percentage), }, { label: , - value: asPercentage(percentageNoChildren), + value: asPercentage(selfCPU.percentage), }, { label: i18n.translate('xpack.profiling.flameGraphInformationWindow.samplesInclusiveLabel', { @@ -76,28 +61,28 @@ export function getImpactRows({ 'xpack.profiling.flameGraphInformationWindow.coreSecondsInclusiveLabel', { defaultMessage: 'Core-seconds' } ), - value: asDuration(coreSeconds), + value: asDuration(totalCPU.coreSeconds), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.coreSecondsExclusiveLabel', { defaultMessage: 'Core-seconds (excl. children)' } ), - value: asDuration(coreSecondsNoChildren), + value: asDuration(selfCPU.coreSeconds), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.annualizedCoreSecondsInclusiveLabel', { defaultMessage: 'Annualized core-seconds' } ), - value: asDuration(annualizedCoreSeconds), + value: asDuration(totalCPU.annualizedCoreSeconds), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.annualizedCoreSecondsExclusiveLabel', { defaultMessage: 'Annualized core-seconds (excl. children)' } ), - value: asDuration(annualizedCoreSecondsNoChildren), + value: asDuration(selfCPU.annualizedCoreSeconds), }, { label: i18n.translate( @@ -106,56 +91,56 @@ export function getImpactRows({ defaultMessage: 'CO2 emission', } ), - value: asWeight(co2), + value: asWeight(totalCPU.co2), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.co2EmissionExclusiveLabel', { defaultMessage: 'CO2 emission (excl. children)' } ), - value: asWeight(co2NoChildren), + value: asWeight(selfCPU.co2), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.annualizedCo2InclusiveLabel', { defaultMessage: 'Annualized CO2' } ), - value: asWeight(annualizedCo2), + value: asWeight(totalCPU.annualizedCo2), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.annualizedCo2ExclusiveLabel', { defaultMessage: 'Annualized CO2 (excl. children)' } ), - value: asWeight(annualizedCo2NoChildren), + value: asWeight(selfCPU.annualizedCo2), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.dollarCostInclusiveLabel', { defaultMessage: 'Dollar cost' } ), - value: asCost(dollarCost), + value: asCost(totalCPU.dollarCost), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.dollarCostExclusiveLabel', { defaultMessage: 'Dollar cost (excl. children)' } ), - value: asCost(dollarCostNoChildren), + value: asCost(selfCPU.dollarCost), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.annualizedDollarCostInclusiveLabel', { defaultMessage: 'Annualized dollar cost' } ), - value: asCost(annualizedDollarCost), + value: asCost(totalCPU.annualizedDollarCost), }, { label: i18n.translate( 'xpack.profiling.flameGraphInformationWindow.annualizedDollarCostExclusiveLabel', { defaultMessage: 'Annualized dollar cost (excl. children)' } ), - value: asCost(annualizedDollarCostNoChildren), + value: asCost(selfCPU.annualizedDollarCost), }, ]; diff --git a/x-pack/plugins/profiling/public/components/topn_functions/function_row.tsx b/x-pack/plugins/profiling/public/components/topn_functions/function_row.tsx index 53f4a3701125ee..4aa4b836eac8ac 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions/function_row.tsx +++ b/x-pack/plugins/profiling/public/components/topn_functions/function_row.tsx @@ -64,10 +64,10 @@ export function FunctionRow({ return ( - {functionRow.diff.rank} + 0 ? 'sortUp' : 'sortDown'} color={color} /> - 0 ? 'sortDown' : 'sortUp'} color={color} /> + {Math.abs(functionRow.diff.rank)} ); @@ -102,16 +102,16 @@ export function FunctionRow({ if ( columnId === TopNFunctionSortField.AnnualizedCo2 && - functionRow.impactEstimates?.annualizedCo2 + functionRow.impactEstimates?.selfCPU?.annualizedCo2 ) { - return
{asWeight(functionRow.impactEstimates.annualizedCo2)}
; + return
{asWeight(functionRow.impactEstimates.selfCPU.annualizedCo2)}
; } if ( columnId === TopNFunctionSortField.AnnualizedDollarCost && - functionRow.impactEstimates?.annualizedDollarCost + functionRow.impactEstimates?.selfCPU?.annualizedDollarCost ) { - return
{asCost(functionRow.impactEstimates.annualizedDollarCost)}
; + return
{asCost(functionRow.impactEstimates.selfCPU.annualizedDollarCost)}
; } return null; diff --git a/x-pack/plugins/profiling/public/components/topn_functions/index.tsx b/x-pack/plugins/profiling/public/components/topn_functions/index.tsx index 52aacc6e4ae7a1..d3528b522443b1 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions/index.tsx +++ b/x-pack/plugins/profiling/public/components/topn_functions/index.tsx @@ -88,8 +88,15 @@ export const TopNFunctionsGrid = forwardRef( comparisonScaleFactor, comparisonTopNFunctions, topNFunctions, + totalSeconds, }); - }, [topNFunctions, comparisonTopNFunctions, comparisonScaleFactor, baselineScaleFactor]); + }, [ + baselineScaleFactor, + comparisonScaleFactor, + comparisonTopNFunctions, + topNFunctions, + totalSeconds, + ]); const { columns, leadingControlColumns } = useMemo(() => { const gridColumns: EuiDataGridColumn[] = [ diff --git a/x-pack/plugins/profiling/public/components/topn_functions/utils.test.ts b/x-pack/plugins/profiling/public/components/topn_functions/utils.test.ts index 98ed1943ecedb5..f0c6ca1a25c8d1 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions/utils.test.ts +++ b/x-pack/plugins/profiling/public/components/topn_functions/utils.test.ts @@ -18,8 +18,8 @@ describe('Top N functions: Utils', () => { it('returns correct value when percentage is 0', () => { expect(getColorLabel(0)).toEqual({ - color: 'danger', - label: '<0.01', + color: 'text', + label: '0%', icon: undefined, }); }); @@ -31,5 +31,13 @@ describe('Top N functions: Utils', () => { icon: 'sortDown', }); }); + + it('returns correct value when percentage is Infinity', () => { + expect(getColorLabel(Infinity)).toEqual({ + color: 'text', + label: undefined, + icon: undefined, + }); + }); }); }); diff --git a/x-pack/plugins/profiling/public/components/topn_functions/utils.ts b/x-pack/plugins/profiling/public/components/topn_functions/utils.ts index bf192de6bd1779..f44454d255601e 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions/utils.ts +++ b/x-pack/plugins/profiling/public/components/topn_functions/utils.ts @@ -10,12 +10,20 @@ import { StackFrameMetadata } from '../../../common/profiling'; import { calculateImpactEstimates } from '../../../common/calculate_impact_estimates'; export function getColorLabel(percent: number) { + if (percent === 0) { + return { color: 'text', label: `0%`, icon: undefined }; + } + const color = percent < 0 ? 'success' : 'danger'; const icon = percent < 0 ? 'sortUp' : 'sortDown'; const isSmallPercent = Math.abs(percent) <= 0.01; - const label = isSmallPercent ? '<0.01' : Math.abs(percent).toFixed(2) + '%'; + const value = isSmallPercent ? '<0.01' : Math.abs(percent).toFixed(2); + + if (isFinite(percent)) { + return { color, label: `${value}%`, icon }; + } - return { color, label, icon: isSmallPercent ? undefined : icon }; + return { color: 'text', label: undefined, icon: undefined }; } export function scaleValue({ value, scaleFactor = 1 }: { value: number; scaleFactor?: number }) { @@ -47,11 +55,13 @@ export function getFunctionsRows({ comparisonScaleFactor, comparisonTopNFunctions, topNFunctions, + totalSeconds, }: { baselineScaleFactor?: number; comparisonScaleFactor?: number; comparisonTopNFunctions?: TopNFunctions; topNFunctions?: TopNFunctions; + totalSeconds: number; }): IFunctionRow[] { if (!topNFunctions || !topNFunctions.TotalCount || topNFunctions.TotalCount === 0) { return []; @@ -64,26 +74,43 @@ export function getFunctionsRows({ return topNFunctions.TopN.filter((topN) => topN.CountExclusive > 0).map((topN, i) => { const comparisonRow = comparisonDataById?.[topN.Id]; - const topNCountExclusiveScaled = scaleValue({ + const scaledSelfCPU = scaleValue({ value: topN.CountExclusive, scaleFactor: baselineScaleFactor, }); + const totalCPUPerc = (topN.CountInclusive / topNFunctions.TotalCount) * 100; + const selfCPUPerc = (topN.CountExclusive / topNFunctions.TotalCount) * 100; + + const impactEstimates = + totalSeconds > 0 + ? calculateImpactEstimates({ + countExclusive: topN.CountExclusive, + countInclusive: topN.CountInclusive, + totalSamples: topNFunctions.TotalCount, + totalSeconds, + }) + : undefined; + function calculateDiff() { if (comparisonTopNFunctions && comparisonRow) { - const comparisonCountExclusiveScaled = scaleValue({ + const comparisonScaledSelfCPU = scaleValue({ value: comparisonRow.CountExclusive, scaleFactor: comparisonScaleFactor, }); + const scaledDiffSamples = scaledSelfCPU - comparisonScaledSelfCPU; + return { rank: topN.Rank - comparisonRow.Rank, - samples: topNCountExclusiveScaled - comparisonCountExclusiveScaled, + samples: scaledDiffSamples, selfCPU: comparisonRow.CountExclusive, totalCPU: comparisonRow.CountInclusive, - selfCPUPerc: topN.selfCPUPerc - comparisonRow.selfCPUPerc, - totalCPUPerc: topN.totalCPUPerc - comparisonRow.totalCPUPerc, - impactEstimates: comparisonRow.impactEstimates, + selfCPUPerc: + selfCPUPerc - (comparisonRow.CountExclusive / comparisonTopNFunctions.TotalCount) * 100, + totalCPUPerc: + totalCPUPerc - + (comparisonRow.CountInclusive / comparisonTopNFunctions.TotalCount) * 100, }; } } @@ -91,12 +118,12 @@ export function getFunctionsRows({ return { rank: topN.Rank, frame: topN.Frame, - samples: topNCountExclusiveScaled, - selfCPUPerc: topN.selfCPUPerc, - totalCPUPerc: topN.totalCPUPerc, + samples: scaledSelfCPU, + selfCPUPerc, + totalCPUPerc, selfCPU: topN.CountExclusive, totalCPU: topN.CountInclusive, - impactEstimates: topN.impactEstimates, + impactEstimates, diff: calculateDiff(), }; }); diff --git a/x-pack/plugins/profiling/public/components/topn_functions_summary/index.tsx b/x-pack/plugins/profiling/public/components/topn_functions_summary/index.tsx index 1a89c93c55e9df..d2057bfafcdcc7 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions_summary/index.tsx +++ b/x-pack/plugins/profiling/public/components/topn_functions_summary/index.tsx @@ -7,7 +7,8 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useMemo } from 'react'; +import { calculateImpactEstimates } from '../../../common/calculate_impact_estimates'; import { TopNFunctions } from '../../../common/functions'; import { asCost } from '../../utils/formatters/as_cost'; import { asWeight } from '../../utils/formatters/as_weight'; @@ -20,6 +21,8 @@ interface Props { baselineScaleFactor?: number; comparisonScaleFactor?: number; isLoading: boolean; + baselineDuration: number; + comparisonDuration: number; } const ESTIMATED_VALUE_LABEL = i18n.translate('xpack.profiling.diffTopNFunctions.estimatedValue', { @@ -29,32 +32,65 @@ const ESTIMATED_VALUE_LABEL = i18n.translate('xpack.profiling.diffTopNFunctions. export function TopNFunctionsSummary({ baselineTopNFunctions, comparisonTopNFunctions, - baselineScaleFactor, - comparisonScaleFactor, + baselineScaleFactor = 1, + comparisonScaleFactor = 1, isLoading, + baselineDuration, + comparisonDuration, }: Props) { - const totalSamplesDiff = calculateBaseComparisonDiff({ - baselineValue: baselineTopNFunctions?.TotalCount || 0, - baselineScaleFactor, - comparisonValue: comparisonTopNFunctions?.TotalCount || 0, - comparisonScaleFactor, - }); + const baselineScaledTotalSamples = baselineTopNFunctions + ? baselineTopNFunctions.TotalCount * baselineScaleFactor + : 0; - const co2EmissionDiff = calculateBaseComparisonDiff({ - baselineValue: baselineTopNFunctions?.impactEstimates?.annualizedCo2 || 0, - baselineScaleFactor, - comparisonValue: comparisonTopNFunctions?.impactEstimates?.annualizedCo2 || 0, - comparisonScaleFactor, - formatValue: asWeight, - }); + const comparisonScaledTotalSamples = comparisonTopNFunctions + ? comparisonTopNFunctions.TotalCount * comparisonScaleFactor + : 0; - const costImpactDiff = calculateBaseComparisonDiff({ - baselineValue: baselineTopNFunctions?.impactEstimates?.annualizedDollarCost || 0, - baselineScaleFactor, - comparisonValue: comparisonTopNFunctions?.impactEstimates?.annualizedDollarCost || 0, - comparisonScaleFactor, - formatValue: asCost, - }); + const { co2EmissionDiff, costImpactDiff, totalSamplesDiff } = useMemo(() => { + const baseImpactEstimates = baselineTopNFunctions + ? // Do NOT scale values here. This is intended to show the exact values spent throughout the year + calculateImpactEstimates({ + countExclusive: baselineTopNFunctions.selfCPU, + countInclusive: baselineTopNFunctions.totalCPU, + totalSamples: baselineTopNFunctions.TotalCount, + totalSeconds: baselineDuration, + }) + : undefined; + + const comparisonImpactEstimates = comparisonTopNFunctions + ? // Do NOT scale values here. This is intended to show the exact values spent throughout the year + calculateImpactEstimates({ + countExclusive: comparisonTopNFunctions.selfCPU, + countInclusive: comparisonTopNFunctions.totalCPU, + totalSamples: comparisonTopNFunctions.TotalCount, + totalSeconds: comparisonDuration, + }) + : undefined; + + return { + totalSamplesDiff: calculateBaseComparisonDiff({ + baselineValue: baselineScaledTotalSamples || 0, + comparisonValue: comparisonScaledTotalSamples || 0, + }), + co2EmissionDiff: calculateBaseComparisonDiff({ + baselineValue: baseImpactEstimates?.totalSamples?.annualizedCo2 || 0, + comparisonValue: comparisonImpactEstimates?.totalSamples.annualizedCo2 || 0, + formatValue: asWeight, + }), + costImpactDiff: calculateBaseComparisonDiff({ + baselineValue: baseImpactEstimates?.totalSamples.annualizedDollarCost || 0, + comparisonValue: comparisonImpactEstimates?.totalSamples.annualizedDollarCost || 0, + formatValue: asCost, + }), + }; + }, [ + baselineDuration, + baselineScaledTotalSamples, + baselineTopNFunctions, + comparisonDuration, + comparisonScaledTotalSamples, + comparisonTopNFunctions, + ]); const data = [ { @@ -62,14 +98,16 @@ export function TopNFunctionsSummary({ defaultMessage: '{label} overall performance by', values: { label: - isLoading || totalSamplesDiff.percentDiffDelta === undefined + isLoading || + totalSamplesDiff.percentDiffDelta === undefined || + totalSamplesDiff.label === undefined ? 'Gained/Lost' : totalSamplesDiff?.percentDiffDelta > 0 ? 'Lost' : 'Gained', }, }) as string, - baseValue: totalSamplesDiff.label || '', + baseValue: totalSamplesDiff.label || '0%', baseIcon: totalSamplesDiff.icon, baseColor: totalSamplesDiff.color, titleHint: ESTIMATED_VALUE_LABEL, diff --git a/x-pack/plugins/profiling/public/components/topn_functions_summary/summary_item.test.ts b/x-pack/plugins/profiling/public/components/topn_functions_summary/summary_item.test.ts new file mode 100644 index 00000000000000..6b82d3d8c819fa --- /dev/null +++ b/x-pack/plugins/profiling/public/components/topn_functions_summary/summary_item.test.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { getValueLable } from './summary_item'; + +describe('Summary item', () => { + it('returns value and percentage', () => { + expect(getValueLable('10', '1%')).toEqual('10 (1%)'); + }); + + it('returns value', () => { + expect(getValueLable('10', undefined)).toEqual('10'); + }); + + it('returns value when perc is an empty string', () => { + expect(getValueLable('10', '')).toEqual('10'); + }); +}); diff --git a/x-pack/plugins/profiling/public/components/topn_functions_summary/summary_item.tsx b/x-pack/plugins/profiling/public/components/topn_functions_summary/summary_item.tsx index bf41625f7224d2..a40ac315de7dab 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions_summary/summary_item.tsx +++ b/x-pack/plugins/profiling/public/components/topn_functions_summary/summary_item.tsx @@ -56,6 +56,10 @@ function BaseValue({ value, icon, color }: { value: string; icon?: string; color ); } +export function getValueLable(value: string, perc?: string) { + return perc ? `${value} (${perc})` : `${value}`; +} + export function SummaryItem({ baseValue, baseIcon, @@ -98,7 +102,7 @@ export function SummaryItem({ {!isLoading && comparisonValue ? ( {comparisonIcon ? : null} - {`${comparisonValue} (${comparisonPerc})`} + {getValueLable(comparisonValue, comparisonPerc)} ) : null} diff --git a/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx b/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx index 107c11e2203ae2..7c1ba0e144383c 100644 --- a/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx +++ b/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx @@ -206,6 +206,8 @@ export function DifferentialTopNFunctionsView() { state.status === AsyncStatus.Loading || comparisonState.status === AsyncStatus.Loading } + baselineDuration={totalSeconds} + comparisonDuration={totalComparisonSeconds} /> @@ -236,13 +238,11 @@ export function DifferentialTopNFunctionsView() {