Skip to content

Commit 69e0074

Browse files
authored
Merge pull request #19800 from mpirvu/cpuutil
Do not update JVM CPU utilization too often
2 parents 677181c + 2eb9482 commit 69e0074

File tree

2 files changed

+55
-48
lines changed

2 files changed

+55
-48
lines changed

runtime/compiler/env/CpuUtilization.cpp

Lines changed: 53 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,19 @@
3333

3434
/*
3535
* Relevant port library API:
36-
*
36+
*
3737
* IDATA j9sysinfo_get_CPU_utilization(struct J9PortLibrary *portLibrary, struct J9SysinfoCPUTime *cpuTime)
38-
*
38+
*
3939
* typedef struct J9SysinfoCPUTime {
4040
*
4141
* I_64 timestamp; // time in nanoseconds from a fixed but arbitrary point in time
4242
* I_64 cpuTime; // cumulative CPU utilization (sum of system and user time in nanoseconds) of all CPUs on the system.
4343
* I_32 numberOfCpus; // number of CPUs as reported by the operating system
44-
*
44+
*
4545
* } J9SysinfoCPUTime;
4646
*
4747
* I_64 j9thread_get_process_times(j9thread_process_time_t * processTime)
48-
*
48+
*
4949
* typedef struct j9thread_process_time_t {
5050
*
5151
* // for consistency sake, times are stored as I_64s
@@ -56,31 +56,30 @@
5656
*/
5757

5858
int32_t CpuUtilization::getCpuUtil(J9JITConfig *jitConfig, J9SysinfoCPUTime *machineCpuStats, j9thread_process_time_t *vmCpuStats)
59-
{
59+
{
6060
IDATA portLibraryStatusSys; // port lib call return value for system cpu usage
6161
IDATA portLibraryStatusVm; // port lib call return value for vm cpu usage
62-
62+
6363
// get access to portlib
6464
PORT_ACCESS_FROM_JITCONFIG(jitConfig);
65-
65+
6666
// get CPU stats for the machine
6767
portLibraryStatusSys = j9sysinfo_get_CPU_utilization(machineCpuStats);
6868
portLibraryStatusVm = j9thread_get_process_times(vmCpuStats);
69-
69+
7070
// if either call failed, disable self
7171
if (portLibraryStatusSys < 0 || portLibraryStatusVm < 0)
7272
{
7373
disable();
7474
return (-1);
7575
}
76-
76+
7777
return 0;
7878
}
7979

8080
/*
8181
* Update the values stored inside the object. Only updates the values
82-
* when called more than _minIntervalLength milliseconds after the last
83-
* update.
82+
* when called more than _minIntervalLength ns after the last update.
8483
*/
8584
int CpuUtilization::updateCpuUtil(J9JITConfig *jitConfig)
8685
{
@@ -89,41 +88,45 @@ int CpuUtilization::updateCpuUtil(J9JITConfig *jitConfig)
8988
{
9089
return (-1);
9190
}
92-
91+
9392
// portlib data
9493
J9SysinfoCPUTime machineCpuStats; // stats for overall CPU usage on machine
9594
j9thread_process_time_t vmCpuStats; // stats for VM's CPU usage
96-
95+
9796
if (getCpuUtil(jitConfig, &machineCpuStats, &vmCpuStats) == -1)
9897
return (-1);
99-
98+
10099
// calculate interval
101-
_prevIntervalLength = machineCpuStats.timestamp - _prevMachineUptime;
102-
100+
int64_t elapsedTime = machineCpuStats.timestamp - _prevMachineUptime;
101+
if (elapsedTime < _minIntervalLength)
102+
return -1; // update not done
103+
104+
_prevIntervalLength = elapsedTime;
105+
103106
// calculate usage and idle percentages
104-
if (_prevIntervalLength > 0)
107+
if (elapsedTime > 0)
105108
{
106109
int64_t prevTotalTimeUsedByVm = _prevVmSysTime + _prevVmUserTime;
107110
int64_t newTotalTimeUsedByVm = vmCpuStats._systemTime + vmCpuStats._userTime;
108-
109-
_cpuUsage = (100 * (machineCpuStats.cpuTime - _prevMachineCpuTime)) / _prevIntervalLength;
111+
112+
_cpuUsage = (100 * (machineCpuStats.cpuTime - _prevMachineCpuTime)) / elapsedTime;
110113
_cpuIdle = 100 * machineCpuStats.numberOfCpus - _cpuUsage;
111-
_vmCpuUsage = (100 * (newTotalTimeUsedByVm - prevTotalTimeUsedByVm)) / _prevIntervalLength;
114+
_vmCpuUsage = (100 * (newTotalTimeUsedByVm - prevTotalTimeUsedByVm)) / elapsedTime;
112115
}
113-
116+
114117
if (machineCpuStats.numberOfCpus > 0)
115118
{
116119
_avgCpuUsage = _cpuUsage / machineCpuStats.numberOfCpus;
117120
}
118-
121+
119122
_avgCpuIdle = 100 - _avgCpuUsage;
120-
123+
121124
// remember values for next time
122125
_prevMachineUptime = machineCpuStats.timestamp;
123126
_prevMachineCpuTime = machineCpuStats.cpuTime;
124127
_prevVmSysTime = vmCpuStats._systemTime;
125128
_prevVmUserTime = vmCpuStats._userTime;
126-
129+
127130
return 0;
128131
} // updateCpuUtil
129132

@@ -134,22 +137,22 @@ int32_t CpuUtilization::updateCpuUsageCircularBuffer(J9JITConfig *jitConfig)
134137
{
135138
return (-1);
136139
}
137-
140+
138141
// portlib data
139142
J9SysinfoCPUTime machineCpuStats; // stats for overall CPU usage on machine
140143
j9thread_process_time_t vmCpuStats; // stats for VM's CPU usage
141-
144+
142145
if (getCpuUtil(jitConfig, &machineCpuStats, &vmCpuStats) == -1)
143146
return (-1);
144-
147+
145148
_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._timeStamp = machineCpuStats.timestamp;
146149
_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._sampleSystemCpu = machineCpuStats.cpuTime;
147150
_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._sampleJvmCpu = vmCpuStats._systemTime + vmCpuStats._userTime;
148-
151+
149152
_cpuUsageCircularBufferIndex = (_cpuUsageCircularBufferIndex + 1)%_cpuUsageCircularBufferSize;
150-
153+
151154
return 0;
152-
155+
153156
} // updateCpuUsageArray
154157

155158
CpuUtilization::CpuUtilization(J9JITConfig *jitConfig):
@@ -160,18 +163,20 @@ CpuUtilization::CpuUtilization(J9JITConfig *jitConfig):
160163
_avgCpuUsage (INITIAL_USAGE),
161164
_cpuIdle (INITIAL_IDLE),
162165
_avgCpuIdle (INITIAL_IDLE),
163-
166+
167+
_minIntervalLength (100000000), // 100 ms
168+
164169
_prevIntervalLength (0),
165-
170+
166171
_prevMachineUptime (0),
167172
_prevMachineCpuTime (0),
168173
_prevVmSysTime (0),
169174
_prevVmUserTime (0),
170-
175+
171176
_isFunctional (true),
172-
177+
173178
_cpuUsageCircularBufferIndex(0)
174-
179+
175180
{
176181
// If the circular buffer size is set to 0, disable the circular buffer
177182
if (TR::Options::_cpuUsageCircularBufferSize == 0)
@@ -180,26 +185,26 @@ CpuUtilization::CpuUtilization(J9JITConfig *jitConfig):
180185
_cpuUsageCircularBuffer = NULL;
181186
return;
182187
}
183-
188+
184189
_isCpuUsageCircularBufferFunctional = true;
185-
190+
186191
// Enforce a minimum size
187192
if (TR::Options::_cpuUsageCircularBufferSize < CPU_UTIL_ARRAY_DEFAULT_SIZE)
188193
_cpuUsageCircularBufferSize = CPU_UTIL_ARRAY_DEFAULT_SIZE;
189194
else
190195
_cpuUsageCircularBufferSize = TR::Options::_cpuUsageCircularBufferSize;
191-
196+
192197
// Allocate the memory for the buffer
193-
_cpuUsageCircularBuffer =
194-
(CpuUsageCircularBuffer *)TR_PersistentMemory::jitPersistentAlloc(sizeof(CpuUsageCircularBuffer)*_cpuUsageCircularBufferSize);
195-
198+
_cpuUsageCircularBuffer =
199+
(CpuUsageCircularBuffer *)TR_PersistentMemory::jitPersistentAlloc(sizeof(CpuUsageCircularBuffer)*_cpuUsageCircularBufferSize);
200+
196201
// Since this happens at bootstrap, if this fails, disable and return
197202
if (!_cpuUsageCircularBuffer)
198203
{
199204
_isCpuUsageCircularBufferFunctional = false;
200205
return;
201206
}
202-
207+
203208
// Initialize the buffer; no need to initialize the _sampleSystemCpu and _sampleJvmCpu
204209
// fields since we can just check if _timeStamp equals 0
205210
for (int32_t i = 0; i < _cpuUsageCircularBufferSize; i++)
@@ -223,24 +228,24 @@ CpuSelfThreadUtilization::CpuSelfThreadUtilization(TR::PersistentInfo *persisten
223228
_isFunctional(true) // optimistic
224229
{
225230
_lowResolutionClockAtLastUpdate = _persistentInfo->getElapsedTime();
226-
_clockTimeAtLastUpdate = getCrtTimeNs();
231+
_clockTimeAtLastUpdate = getCrtTimeNs();
227232
_cpuTimeAtLastUpdate = 0; //getCpuTimeNow(); The constructor might not be called by the compilation thread
228-
}
233+
}
229234

230-
void CpuSelfThreadUtilization::setAsUnfunctional()
231-
{
235+
void CpuSelfThreadUtilization::setAsUnfunctional()
236+
{
232237
_isFunctional = false;
233238
// set an invalid value which sometimes may obviate the need to test _isFunctional
234239
_cpuTimeDuringLastInterval = _cpuTimeDuringSecondLastInterval = _cpuTimeAtLastUpdate = -1;
235240
_lastCpuUtil = _secondLastCpuUtil = - 1;
236241
// TODO: place a VM style trace point
237-
}
242+
}
238243

239244
bool CpuSelfThreadUtilization::update() // return true if an update was performed
240245
{
241246
if (!_isFunctional)
242247
return false;
243-
// Refuse to update if not enough time has passed;
248+
// Refuse to update if not enough time has passed;
244249
// use the lower resolution time to avoid overhead
245250
if ((_persistentInfo->getElapsedTime() - _lowResolutionClockAtLastUpdate)*1000000 < _minMeasurementIntervalLength)
246251
return false;

runtime/compiler/env/CpuUtilization.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ class CpuUtilization
100100
int32_t _avgCpuUsage; // percentage of time each processor is used on average (0..100)
101101
int32_t _avgCpuIdle; // percentage of time each processor is idle on average (0..100)
102102

103+
const int64_t _minIntervalLength; // Postpone the update until this many ns passed since the last update operation
104+
103105
int64_t _prevIntervalLength; // the duration (in ns) of the last update interval
104106

105107
// values recorded at start of this update interval

0 commit comments

Comments
 (0)