forked from elastic/elasticsearch
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4f38465
commit 9df6232
Showing
3 changed files
with
164 additions
and
60 deletions.
There are no files selected for viewing
56 changes: 56 additions & 0 deletions
56
x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Resampler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.profiling; | ||
|
||
import java.util.Random; | ||
import java.util.random.RandomGenerator; | ||
|
||
class Resampler { | ||
private final boolean requiresResampling; | ||
private final RandomGenerator r; | ||
private final double adjustedSampleRate; | ||
private final double p; | ||
|
||
Resampler(GetStackTracesRequest request, double sampleRate, long totalCount) { | ||
// Manually reduce sample count if totalCount exceeds sampleSize by 10%. | ||
if (totalCount > request.getSampleSize() * 1.1) { | ||
this.requiresResampling = true; | ||
// Make the RNG predictable to get reproducible results. | ||
this.r = createRandom(request); | ||
this.p = (double) request.getSampleSize() / totalCount; | ||
} else { | ||
this.requiresResampling = false; | ||
this.r = null; | ||
this.p = 1.0d; | ||
} | ||
// TODO: Just use the sample rate as is once all resampling is done server-side | ||
this.adjustedSampleRate = request.isAdjustSampleCount() ? sampleRate : 1.0d; | ||
} | ||
|
||
protected RandomGenerator createRandom(GetStackTracesRequest request) { | ||
return new Random(request.hashCode()); | ||
} | ||
|
||
public int adjustSampleCount(int originalCount) { | ||
int rawCount; | ||
if (requiresResampling) { | ||
rawCount = 0; | ||
for (int i = 0; i < originalCount; i++) { | ||
if (r.nextDouble() < p) { | ||
rawCount++; | ||
} | ||
} | ||
} else { | ||
rawCount = originalCount; | ||
} | ||
// Adjust the sample counts from down-sampled to fully sampled. | ||
// Be aware that downsampling drops entries from stackTraceEvents, so that | ||
// the sum of the upscaled count values is less that totalCount. | ||
return (int) Math.floor(rawCount / (p * adjustedSampleRate)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.profiling; | ||
|
||
import org.elasticsearch.test.ESTestCase; | ||
|
||
import java.util.random.RandomGenerator; | ||
|
||
public class ResamplerTests extends ESTestCase { | ||
|
||
private Resampler createResampler(GetStackTracesRequest request, double sampleRate, long totalCount) { | ||
return new Resampler(request, sampleRate, totalCount) { | ||
@Override | ||
protected RandomGenerator createRandom(GetStackTracesRequest request) { | ||
return DeterministicRandom.of(0.0d, 1.0d); | ||
} | ||
}; | ||
} | ||
|
||
public void testNoResamplingNoSampleRateAdjustment() { | ||
// corresponds to profiling-events-5pow01 | ||
double sampleRate = 1.0d / Math.pow(5.0d, 1); | ||
int requestedSamples = 20_000; | ||
int actualTotalSamples = 10_000; | ||
|
||
GetStackTracesRequest request = new GetStackTracesRequest(requestedSamples, null); | ||
request.setAdjustSampleCount(false); | ||
|
||
Resampler resampler = createResampler(request, sampleRate, actualTotalSamples); | ||
|
||
int actualSamplesSingleTrace = 5_000; | ||
assertEquals(5_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); | ||
} | ||
|
||
public void testNoResamplingButAdjustSampleRate() { | ||
// corresponds to profiling-events-5pow01 | ||
double sampleRate = 1.0d / Math.pow(5.0d, 1); | ||
int requestedSamples = 20_000; | ||
int actualTotalSamples = 10_000; | ||
|
||
GetStackTracesRequest request = new GetStackTracesRequest(requestedSamples, null); | ||
request.setAdjustSampleCount(true); | ||
|
||
Resampler resampler = createResampler(request, sampleRate, actualTotalSamples); | ||
|
||
int actualSamplesSingleTrace = 5_000; | ||
assertEquals(25_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); | ||
} | ||
|
||
public void testResamplingNoSampleRateAdjustment() { | ||
// corresponds to profiling-events-5pow01 | ||
double sampleRate = 1.0d / Math.pow(5.0d, 1); | ||
int requestedSamples = 20_000; | ||
int actualTotalSamples = 40_000; | ||
|
||
GetStackTracesRequest request = new GetStackTracesRequest(requestedSamples, null); | ||
request.setAdjustSampleCount(false); | ||
|
||
Resampler resampler = createResampler(request, sampleRate, actualTotalSamples); | ||
|
||
int actualSamplesSingleTrace = 20_000; | ||
assertEquals(20_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); | ||
} | ||
|
||
public void testResamplingAndSampleRateAdjustment() { | ||
// corresponds to profiling-events-5pow01 | ||
double sampleRate = 1.0d / Math.pow(5.0d, 1); | ||
int requestedSamples = 20_000; | ||
int actualTotalSamples = 40_000; | ||
|
||
GetStackTracesRequest request = new GetStackTracesRequest(requestedSamples, null); | ||
request.setAdjustSampleCount(true); | ||
|
||
Resampler resampler = createResampler(request, sampleRate, actualTotalSamples); | ||
|
||
int actualSamplesSingleTrace = 20_000; | ||
assertEquals(100_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); | ||
} | ||
|
||
private static class DeterministicRandom implements RandomGenerator { | ||
private final double[] values; | ||
private int idx; | ||
|
||
private DeterministicRandom(double... values) { | ||
this.values = values; | ||
this.idx = 0; | ||
} | ||
|
||
public static RandomGenerator of(double... values) { | ||
return new DeterministicRandom(values); | ||
} | ||
|
||
@Override | ||
public long nextLong() { | ||
return Double.doubleToLongBits(nextDouble()); | ||
} | ||
|
||
@Override | ||
public double nextDouble() { | ||
return values[idx++ % values.length]; | ||
} | ||
} | ||
} |