Skip to content

Commit dc8f2d0

Browse files
use geometric sampling distribution. Adjust debt testcase
1 parent d1386ea commit dc8f2d0

File tree

3 files changed

+86
-34
lines changed

3 files changed

+86
-34
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThrottlerWindow.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
import com.oracle.svm.core.jdk.UninterruptibleUtils;
2929

30+
import static java.lang.Math.log;
31+
3032
public class JfrThrottlerWindow {
3133
// reset every rotation
3234
public UninterruptibleUtils.AtomicLong measuredPopSize; // already volatile
@@ -83,15 +85,14 @@ public long samplesExpected() {
8385

8486
public void configure(long debt, double projectedPopSize) {
8587
this.debt = debt;
86-
if (projectedPopSize <= samplesPerWindow) {
88+
if (projectedPopSize <= samplesExpected()) {
8789
samplingInterval = 1;
8890
} else {
8991
// It's important to round *up* otherwise we risk violating the upper bound
90-
samplingInterval = (long) Math.ceil(projectedPopSize / (double) samplesExpected()); // ***
91-
// TODO:
92-
// geometric
93-
// distribution
94-
// stuff
92+
// TODO geometric
93+
// samplingInterval = (long) Math.ceil(projectedPopSize / (double) samplesExpected());
94+
double projectedProbability = (double) samplesExpected() / projectedPopSize;
95+
samplingInterval = nextGeometric(projectedProbability, Math.random());
9596
}
9697
// activeWindowSampleLimit is either projectedPopSize or samplesExpected() (if
9798
// projectedPopSize < samplesPerWindow)
@@ -108,6 +109,15 @@ public void configure(long debt, double projectedPopSize) {
108109

109110
}
110111

112+
long nextGeometric(double p, double u) { // *** is P is larger, then its more likely sampling
113+
// interval is smaller
114+
if (u == 0.0) {
115+
u = 0.01;
116+
}
117+
// Inverse CDF for the geometric distribution.
118+
return (long) Math.ceil(log(1.0 - u) / log(1.0 - p));
119+
}
120+
111121
public boolean isExpired() {
112122
if (isTest) {
113123
// There is a need to mock JfrTicks for testing.

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ObjectAllocationSampleEvent.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
3838
import com.oracle.svm.core.threadlocal.FastThreadLocalLong;
3939
import com.oracle.svm.core.thread.PlatformThreads;
40+
import com.oracle.svm.core.thread.JavaThreads;
4041

4142
public class ObjectAllocationSampleEvent {
4243
private static final FastThreadLocalLong lastAllocationSize = FastThreadLocalFactory.createLong("ObjectAllocationSampleEvent.lastAllocationSize");
@@ -53,7 +54,7 @@ public static void emit(long startTicks, Class<?> clazz) {
5354
@Uninterruptible(reason = "Accesses a JFR buffer.")
5455
private static void emit0(long startTicks, Class<?> clazz) {
5556
if (JfrEvent.ObjectAllocationSample.shouldEmit()) {
56-
long currentAllocationSize = PlatformThreads.getThreadAllocatedBytes(com.oracle.svm.core.thread.JavaThreads.getCurrentThreadId());
57+
long currentAllocationSize = PlatformThreads.getThreadAllocatedBytes(JavaThreads.getCurrentThreadId());
5758

5859
JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class);
5960
JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data);

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestThrottler.java

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,45 @@ public void testEWMA() {
176176
* Window lookback for this test is 25. Window duration is 1 second. Window divisor is default
177177
* of 5.
178178
*/
179+
// @Test
180+
// public void testDebt() {
181+
// final long samplesPerWindow = 10;
182+
// final long actualSamplesPerWindow = 50;
183+
// JfrThrottler throttler = new JfrThrottler(mutex);
184+
// throttler.beginTest(samplesPerWindow * WINDOWS_PER_PERIOD, WINDOWS_PER_PERIOD *
185+
// WINDOW_DURATION_MS);
186+
//
187+
// for (int p = 0; p < 50; p++) {
188+
// for (int i = 0; i < actualSamplesPerWindow; i++) {
189+
// throttler.sample();
190+
// }
191+
// expireAndRotate(throttler);
192+
// }
193+
// // now the sampling interval must be 50 / 10 = 5
194+
// assertTrue("Sampling interval is incorrect:" + throttler.getActiveWindowSamplingInterval(),
195+
// throttler.getActiveWindowSamplingInterval() == 5);
196+
//
197+
// // create debt by under sampling. Instead of 50, only sample 20 times. Debt should be 10 -
198+
// // (20/5) = 6
199+
// // samples
200+
// for (int i = 0; i < 20; i++) {
201+
// throttler.sample();
202+
// }
203+
// expireAndRotate(throttler);
204+
// assertTrue("Should have debt from under sampling.", throttler.getActiveWindowDebt() == 6);
205+
// // sampling interval should be 3 now. Take no samples and rotate. Results in accumulated
206+
// // debt 6 + 10
207+
// expireAndRotate(throttler);
208+
// assertTrue("Should have accumulated debt from under sampling consecutively.",
209+
// throttler.getActiveWindowDebt() == 16);
210+
// expireAndRotate(throttler);
211+
// expireAndRotate(throttler);
212+
// assertTrue("Debt is so high we should not skip any samples now.",
213+
// throttler.getActiveWindowSamplingInterval() == 1);
214+
// expireAndRotate(throttler);
215+
// assertTrue("Debt should be forgiven at beginning of new period.", throttler.getActiveWindowDebt()
216+
// == 0);
217+
// }
179218
@Test
180219
public void testDebt() {
181220
final long samplesPerWindow = 10;
@@ -189,26 +228,36 @@ public void testDebt() {
189228
}
190229
expireAndRotate(throttler);
191230
}
192-
// now the sampling interval must be 50 / 10 = 5
193-
assertTrue("Sampling interval is incorrect:" + throttler.getActiveWindowSamplingInterval(), throttler.getActiveWindowSamplingInterval() == 5);
194231

195-
// create debt by under sampling. Instead of 50, only sample 20 times. Debt should be 10 -
196-
// (20/5) = 6
197-
// samples
198-
for (int i = 0; i < 20; i++) {
232+
// Do not sample for this window. Rotate.
233+
expireAndRotate(throttler);
234+
235+
// Debt should be at least 10 because we took no samples last window.
236+
long debt = throttler.getActiveWindowDebt();
237+
assertTrue("Should have debt from under sampling.", debt >= 10);
238+
239+
// Limit max potential samples to half samplesPerWindow. Meaning debt must increase by at
240+
// least samplesPerWindow/2.
241+
for (int i = 0; i < samplesPerWindow / 2; i++) {
199242
throttler.sample();
200243
}
201244
expireAndRotate(throttler);
202-
assertTrue("Should have debt from under sampling.", throttler.getActiveWindowDebt() == 6);
203-
// sampling interval should be 3 now. Take no samples and rotate. Results in accumulated
204-
// debt 6 + 10
205-
expireAndRotate(throttler);
206-
assertTrue("Should have accumulated debt from under sampling consecutively.", throttler.getActiveWindowDebt() == 16);
207-
expireAndRotate(throttler);
208-
expireAndRotate(throttler);
209-
assertTrue("Debt is so high we should not skip any samples now.", throttler.getActiveWindowSamplingInterval() == 1);
245+
assertTrue("Should have debt from under sampling.", throttler.getActiveWindowDebt() >= debt + samplesPerWindow / 2);
246+
247+
// Window lookback is 25. Do not sample for 25 windows.
248+
for (int i = 0; i < 25; i++) {
249+
expireAndRotate(throttler);
250+
}
251+
252+
// At this point sampling interval must be 1 because the projected population must be 0.
253+
for (int i = 0; i < (samplesPerWindow + samplesPerWindow * WINDOWS_PER_PERIOD); i++) {
254+
throttler.sample();
255+
}
256+
257+
assertFalse(throttler.sample());
258+
210259
expireAndRotate(throttler);
211-
assertTrue("Debt should be forgiven at beginning of new period.", throttler.getActiveWindowDebt() == 0);
260+
assertTrue(throttler.getActiveWindowDebt() == 0);
212261
}
213262

214263
/**
@@ -241,13 +290,9 @@ public void testZeroRate() throws Throwable {
241290
assertTrue(throttler.sample());
242291

243292
// Test applying throttling settings to an in-progress recording
244-
Recording recording = startRecording(new String[]{}, null, new HashMap<>()); // don't use
245-
// default
246-
// configuration
247-
// because it
248-
// includes
249-
// ObjectAllocationSample
250-
// by default
293+
// Avoid using default configuration becuase it enables ObjectAllocationSample events by
294+
// default
295+
Recording recording = startRecording(new String[]{}, null, new HashMap<>());
251296
recording.enable(JfrEvent.ObjectAllocationSample.getName()).with("throttle", "0/s");
252297
final int alignedHeapChunkSize = com.oracle.svm.core.util.UnsignedUtils.safeToInt(HeapParameters.getAlignedHeapChunkSize());
253298
allocateCharArray(alignedHeapChunkSize);
@@ -323,8 +368,7 @@ private void testDistribution(IncomingPopulation incomingPopulation, int sampleP
323368
final int windowCount = 10000;
324369
final int expectedSamplesPerWindow = 50;
325370
final int expectedSamples = expectedSamplesPerWindow * windowCount;
326-
final int windowLookBackCount = 50;
327-
final double maxSampleBias = 0.11;
371+
328372
JfrThrottler throttler = new JfrThrottler(mutex);
329373
throttler.beginTest(expectedSamplesPerWindow * WINDOWS_PER_PERIOD, windowDurationMs * WINDOWS_PER_PERIOD);
330374

@@ -347,13 +391,11 @@ private void testDistribution(IncomingPopulation incomingPopulation, int sampleP
347391
expireAndRotate(throttler);
348392
}
349393
int targetSampleSize = samplePointsPerWindow * windowCount;
350-
System.out.println("Population size:" + populationSize + " Sample size: " + sampleSize);
351394
expectNear(targetSampleSize, sampleSize, expectedSamples * errorFactor);
352395
assertDistributionProperties(distributionSlots, population, sample, populationSize, sampleSize);
353396
}
354397

355398
private static void expectNear(double value1, double value2, double error) {
356-
// System.out.println(value1 +" "+ value2);
357399
assertTrue(Math.abs(value1 - value2) <= error);
358400
}
359401

@@ -381,7 +423,6 @@ private static void assertDistributionProperties(int distributionSlots, int[] po
381423
sampleVariance = sampleVariance / (sampleSize - 1);
382424
double populationStdev = Math.sqrt(populationVariance);
383425
double sampleStdev = Math.sqrt(sampleVariance);
384-
// System.out.println("populationStdev:"+populationStdev +" sampleStdev:"+sampleStdev );
385426
expectNear(populationStdev, sampleStdev, 0.5); // 0.5 value copied from Hotspot test
386427
expectNear(populationMean, sampleMean, populationStdev);
387428
}

0 commit comments

Comments
 (0)