forked from firefox-devtools/profiler
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpu.ts
More file actions
141 lines (127 loc) · 4.77 KB
/
cpu.ts
File metadata and controls
141 lines (127 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import {
ensureExists,
assertExhaustiveCheck,
} from 'firefox-profiler/utils/types';
import { numberSeriesToDeltas } from 'firefox-profiler/utils/number-series';
import type { RawThread, Profile } from 'firefox-profiler/types';
/**
* Compute the max CPU cycles per ms for the thread. Should only be called when
* the cpu delta unit is 'variable CPU cycles'.
* This computes the max value before the threadCPUDelta processing -
* the threadCPUDelta processing wouldn't do anything other than remove nulls
* anyway, because if the unit is 'variable CPU cycles' then we don't do any
* clamping.
*/
function _computeMaxVariableCPUCyclesPerMs(threads: RawThread[]): number {
let referenceCPUDeltaPerMs = 0;
for (let threadIndex = 0; threadIndex < threads.length; threadIndex++) {
const { samples } = threads[threadIndex];
const { threadCPUDelta } = samples;
if (!threadCPUDelta) {
// The thread have any ThreadCPUDelta values. Older profiles don't have
// this information.
continue;
}
const timeDeltas =
samples.time !== undefined
? numberSeriesToDeltas(samples.time)
: ensureExists(samples.timeDeltas);
// Ignore the first CPU delta value; it's meaningless because there is no
// previous sample.
for (let i = 1; i < samples.length; i++) {
const sampleTimeDeltaInMs = timeDeltas[i];
if (sampleTimeDeltaInMs !== 0) {
const cpuDeltaPerMs = (threadCPUDelta[i] || 0) / sampleTimeDeltaInMs;
referenceCPUDeltaPerMs = Math.max(
referenceCPUDeltaPerMs,
cpuDeltaPerMs
);
}
}
}
return referenceCPUDeltaPerMs;
}
/**
* Returns the expected cpu delta per millisecond if cpu is at 100%.
*
* Returns 1 if the profile does not use cpu deltas.
*
* If the profile uses CPU deltas given in 'variable CPU cycles', then we check
* all threads and return the maximum observed cpu delta per millisecond value,
* which becomes the reference for 100% CPU.
*
* If the profile uses CPU deltas in microseconds or nanoseconds, the we return
* the conversion factor to milliseconds.
*/
export function computeReferenceCPUDeltaPerMs(profile: Profile): number {
const sampleUnits = profile.meta.sampleUnits;
if (!sampleUnits) {
return 1;
}
const threadCPUDeltaUnit = sampleUnits.threadCPUDelta;
switch (threadCPUDeltaUnit) {
case 'µs':
// ms to µs multiplier
return 1000;
case 'ns':
// ms to ns multiplier
return 1000000;
case 'variable CPU cycles':
return _computeMaxVariableCPUCyclesPerMs(profile.threads);
default:
throw assertExhaustiveCheck(
threadCPUDeltaUnit,
'Unhandled threadCPUDelta unit in computeReferenceCPUDeltaPerMs.'
);
}
}
/**
* Computes the threadCPUPercent column for the SamplesTable.
*
* The CPU percentage is a number between 0 and 100, and describes the CPU use between
* the previous sample time and the current sample time. It is the ratio of cpu
* time to elapsed wall clock time, times 100.
*
* This function synthesizes 100% values if `samples` does not have a `threadCPUDelta`
* column.
*
* The returned array has length samples.length + 1, and the first and last elements are
* always zero.
*/
export function computeThreadCPUPercent(
threadCPUDelta: Array<number | null>,
timeDeltas: number[],
referenceCPUDeltaPerMs: number
): Uint8Array {
const threadCPUPercent: Uint8Array = new Uint8Array(
threadCPUDelta.length + 1
);
// Ignore threadCPUDelta[0] and set threadCPUPercent[0] to zero - there is no
// previous sample so there is no meaningful value we could compute here.
threadCPUPercent[0] = 0;
// For the rest of the samples, compute the ratio based on the CPU delta and
// on the elapsed time between samples (timeDeltas[i]).
for (let i = 1; i < threadCPUDelta.length; i++) {
const referenceCpuDelta = referenceCPUDeltaPerMs * timeDeltas[i];
const cpuDelta = threadCPUDelta[i];
if (cpuDelta === null || referenceCpuDelta === 0) {
// Default to 100% CPU if the CPU delta isn't known or if no time has
// elapsed between samples.
// In profiles from Firefox, values at the beginning of threadCPUDelta can
// be null if the samples at the beginning were collected by the base
// profiler, which doesn't support collecting CPU delta information yet,
// see bug 1756519.
threadCPUPercent[i] = 100;
continue;
}
// Limit values to 1.0.
threadCPUPercent[i] =
cpuDelta <= referenceCpuDelta
? Math.round((100 * cpuDelta) / referenceCpuDelta)
: 100;
}
return threadCPUPercent;
}