[Profiling] Update coloring logic (#140846)

This commit is contained in:
Dario Gieselaar 2022-09-19 09:11:33 +02:00 committed by GitHub
parent 416a4733e1
commit 2b0dddb8de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 41 deletions

View file

@ -8,35 +8,38 @@ import { getInterpolationValue } from './get_interpolation_value';
describe('getInterpolationValue', () => {
it('returns 0 for no change', () => {
expect(getInterpolationValue(100, 100)).toBe(0);
expect(getInterpolationValue(8, 8)).toBe(0);
});
it('returns -1 when the background is undefined', () => {
expect(getInterpolationValue(100, undefined)).toBe(-1);
expect(getInterpolationValue(8, undefined)).toBe(1);
});
it('returns -1 when the background is 0', () => {
expect(getInterpolationValue(100, 0)).toBe(-1);
expect(getInterpolationValue(8, 0)).toBe(1);
});
it('returns 0 when both values are 0', () => {
expect(getInterpolationValue(0, 0)).toBe(0);
it('returns 0 when both values are equal', () => {
expect(getInterpolationValue(1, 1)).toBe(0);
});
it('returns the correct value on positive changes', () => {
expect(getInterpolationValue(100, 120)).toBeCloseTo(0.1);
expect(getInterpolationValue(80, 100)).toBeCloseTo(0.125);
expect(getInterpolationValue(90, 270)).toBeCloseTo(1);
it('returns the correct positive change', () => {
expect(getInterpolationValue(8, 5)).toBe(0.375);
});
it('returns the correct value on negative changes', () => {
expect(getInterpolationValue(160, 120)).toBeCloseTo(-0.5);
expect(getInterpolationValue(150, 100)).toBeCloseTo(-2 / 3);
it('returns the correct negative change', () => {
expect(getInterpolationValue(5, 8)).toBe(-0.6);
});
it('clamps the value', () => {
expect(getInterpolationValue(90, 360)).toBeCloseTo(1);
expect(getInterpolationValue(360, 90)).toBeCloseTo(-1);
it('returns the correct positive change with a denominator', () => {
expect(getInterpolationValue(10, 8, 50)).toBe(0.04);
});
it('returns the correct negative change with a denominator', () => {
expect(getInterpolationValue(8, 10, 50)).toBe(-0.04);
});
it('clamps changes', () => {
expect(getInterpolationValue(5, 12)).toBe(-1);
});
});

View file

@ -7,15 +7,14 @@
import { clamp } from 'lodash';
const MAX_POSITIVE_CHANGE = 2;
const MAX_NEGATIVE_CHANGE = 0.5;
export function getInterpolationValue(foreground: number, background: number | null | undefined) {
if (background === null || background === undefined) {
return -1;
export function getInterpolationValue(
foreground: number,
background: number | undefined,
denominator: number = foreground
) {
if (background === undefined) {
return 1;
}
const change = clamp(background / foreground - 1, -MAX_NEGATIVE_CHANGE, MAX_POSITIVE_CHANGE) || 0;
return change >= 0 ? change / MAX_POSITIVE_CHANGE : change / MAX_NEGATIVE_CHANGE;
return clamp((foreground - background) / denominator, -1, 1);
}

View file

@ -6,7 +6,7 @@
*/
import { ColumnarViewModel } from '@elastic/charts';
import d3 from 'd3';
import { uniqueId } from 'lodash';
import { sum, uniqueId } from 'lodash';
import { ElasticFlameGraph, FlameGraphComparisonMode, rgbToRGBA } from '../../../common/flamegraph';
import { getInterpolationValue } from './get_interpolation_value';
@ -57,28 +57,44 @@ export function getFlamegraphModel({
};
});
const positiveChangeInterpolator = d3.interpolateRgb(colorNeutral, colorDanger);
const positiveChangeInterpolator = d3.interpolateRgb(colorNeutral, colorSuccess);
const negativeChangeInterpolator = d3.interpolateRgb(colorNeutral, colorSuccess);
const negativeChangeInterpolator = d3.interpolateRgb(colorNeutral, colorDanger);
const comparisonExclusive: number[] = [];
const comparisonInclusive: number[] = [];
// per @thomasdullien:
// In "relative" mode: Take the percentage of CPU time consumed by block A and subtract
// the percentage of CPU time consumed by block B. If the number is positive, linearly
// interpolate a color between grey and green, with the delta relative to the size of
// block A as percentage.
// Example 1: BlockA 8%, BlockB 5%, delta 3%. This represents a 3/8th reduction, 37.5%
// of the original time, so the color should be 37.5% "green".
// Example 2: BlockA 5%, BlockB 8%, delta -3%. This represents a 3/5th worsening of BlockA,
// so the color should be 62.5% "red". In "absolute" mode: Take the number of samples in
// blockA, subtract the number of samples in blockB. Divide the result by the number of
// samples in the first graph. The result is the amount of saturation for the color.
// Example 3: BlockA 10k samples, BlockB 8k samples, total samples 50k. 10k-8k = 2k, 2k/50k
// = 4%, therefore 4% "green".
const totalSamples = sum(primaryFlamegraph.CountExclusive);
const comparisonTotalSamples = sum(comparisonFlamegraph.CountExclusive);
primaryFlamegraph.ID.forEach((nodeID, index) => {
const countInclusive = primaryFlamegraph.CountInclusive[index];
const countExclusive = primaryFlamegraph.CountExclusive[index];
const samples = primaryFlamegraph.Value[index];
const comparisonSamples = comparisonNodesById[nodeID]?.Value as number | undefined;
const comparisonNode = comparisonNodesById[nodeID];
comparisonExclusive![index] = comparisonNode?.CountExclusive;
comparisonInclusive![index] = comparisonNode?.CountInclusive;
const [foreground, background] =
const foreground =
comparisonMode === FlameGraphComparisonMode.Absolute ? samples : samples / totalSamples;
const background =
comparisonMode === FlameGraphComparisonMode.Absolute
? [countInclusive, comparisonNode?.CountInclusive]
: [countExclusive, comparisonNode?.CountExclusive];
? comparisonSamples
: (comparisonSamples ?? 0) / comparisonTotalSamples;
const interpolationValue = getInterpolationValue(foreground, background);
const denominator =
comparisonMode === FlameGraphComparisonMode.Absolute ? totalSamples : foreground;
const interpolationValue = getInterpolationValue(foreground, background, denominator);
const nodeColor =
interpolationValue >= 0