Skip to content

Commit ec43699

Browse files
authored
[Profiler] Compare fixed and Poisson threshold allocation sampling/upscaling (#4016)
* Add Poisson sampling/upscaling strategy * Compare Poisson and fixed threshold sampling/upscaling results
1 parent 28b18e4 commit ec43699

File tree

12 files changed

+329
-43
lines changed

12 files changed

+329
-43
lines changed

profiler/src/Tools/AllocSimulator/BinaryFileAllocProvider.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ private void ReadAllocations(FileStream fileStream, ref int pos)
139139
pos += 8; // skip the read 2 x 4 bytes
140140
}
141141
}
142-
143142
}
144143
}
145144
}

profiler/src/Tools/AllocSimulator/Engine.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ namespace AllocSimulator
99
{
1010
public class Engine : IEngine
1111
{
12-
private const long Threshold = 100 * 1024;
13-
1412
private IAllocProvider _provider;
1513
private ISampler _sampler;
14+
private IUpscaler _upscaler;
1615
private long _cumulatedSize;
1716
private IDictionary<string, AllocInfo> _allocations;
1817

1918
// TODO support a list of ISampler to compare non upscaled and upscaled for example
20-
public Engine(IAllocProvider provider, ISampler sampler)
19+
public Engine(IAllocProvider provider, ISampler sampler, IUpscaler upscaler)
2120
{
2221
_provider = provider;
2322
_sampler = sampler;
23+
_upscaler = upscaler;
2424
_cumulatedSize = 0;
2525
_allocations = new Dictionary<string, AllocInfo>();
2626
}
@@ -61,21 +61,16 @@ public void Run()
6161

6262
info.Size = info.Size + alloc.Size;
6363
info.Count = info.Count + alloc.Count;
64+
_cumulatedSize += alloc.Size;
6465

65-
if (ShouldSample(alloc.Size))
66+
if (_sampler.ShouldSample(alloc.Size))
6667
{
67-
_sampler.OnAllocationTick(alloc.Type, alloc.Key, alloc.Size, _cumulatedSize);
68+
_upscaler.OnAllocationTick(alloc.Type, alloc.Key, alloc.Size, _cumulatedSize);
6869
_cumulatedSize = 0;
6970
}
7071
}
7172
}
7273
}
73-
74-
private bool ShouldSample(long size)
75-
{
76-
_cumulatedSize += size;
77-
return (_cumulatedSize >= Threshold);
78-
}
7974
}
8075
}
8176

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// <copyright file="FixedSampler.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
4+
// </copyright>
5+
6+
namespace AllocSimulator
7+
{
8+
public class FixedSampler : ISampler
9+
{
10+
// the current .NET fixed threshold for AllocationTick is 100 KB
11+
private const long Threshold = 100 * 1024;
12+
13+
private long _totalAllocatedAmount;
14+
15+
public bool ShouldSample(long size)
16+
{
17+
_totalAllocatedAmount += size;
18+
var shouldSample = _totalAllocatedAmount > Threshold;
19+
20+
if (shouldSample)
21+
{
22+
_totalAllocatedAmount = 0;
23+
}
24+
25+
return shouldSample;
26+
}
27+
}
28+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// <copyright file="FixedUpscaler.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
4+
// </copyright>
5+
6+
namespace AllocSimulator
7+
{
8+
public class FixedUpscaler : UpscalerBase
9+
{
10+
protected override void OnUpscale(AllocInfo sampled, ref AllocInfo upscaled)
11+
{
12+
upscaled.Size = (long)((sampled.Size * _totalAllocatedAmount) / _totalSampledSize);
13+
upscaled.Count = (int)((sampled.Count * _totalAllocatedAmount) / _totalSampledSize);
14+
}
15+
}
16+
}

profiler/src/Tools/AllocSimulator/ISampler.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ namespace AllocSimulator
77
{
88
public interface ISampler
99
{
10-
public void OnAllocationTick(string type, int key, long size, long allocationsAmount);
11-
12-
public IEnumerable<AllocInfo> GetAllocs();
10+
public bool ShouldSample(long size);
1311
}
1412
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// <copyright file="IUpscaler.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
4+
// </copyright>
5+
6+
namespace AllocSimulator
7+
{
8+
public interface IUpscaler
9+
{
10+
public void OnAllocationTick(string type, int key, long size, long allocationsAmount);
11+
12+
public IEnumerable<AllocInfo> GetAllocs();
13+
}
14+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// <copyright file="JavaPoissonSampler.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
4+
// </copyright>
5+
6+
namespace AllocSimulator
7+
{
8+
// from https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/threadHeapSampler.cpp
9+
public class JavaPoissonSampler : ISampler
10+
{
11+
private const ulong MeanSamplingSize = 512 * 1024; // 512 KB is the mean of the distribution
12+
private const double MinusLog2 = -0.6931471805599453; // = - ln(2)
13+
14+
// for fast randomizer
15+
private const ulong PrngMult = 0x5DEECE66D;
16+
private const ulong PrngAdd = 0xB;
17+
private const ulong PrngModMask = (1 << 48) - 1;
18+
19+
private ulong _totalAllocatedAmount;
20+
private ulong _threshold; // number of bytes until the next sample
21+
private ulong _random;
22+
23+
public JavaPoissonSampler()
24+
{
25+
_random = (ulong)Random.Shared.NextInt64(1, 281474976710656); // from 1 to 2^48
26+
_threshold = GetNextThreshold();
27+
}
28+
29+
public bool ShouldSample(long size)
30+
{
31+
_totalAllocatedAmount += (ulong)size;
32+
var shouldSample = _totalAllocatedAmount > _threshold;
33+
34+
if (shouldSample)
35+
{
36+
_totalAllocatedAmount = 0;
37+
_threshold = GetNextThreshold();
38+
}
39+
40+
return shouldSample;
41+
}
42+
43+
// Generates a geometric variable with the specified mean (512K by default).
44+
// This is done by generating a random number between 0 and 1 and applying
45+
// the inverse cumulative distribution function for an exponential.
46+
// Specifically: Let m be the inverse of the sample interval, then
47+
// the probability distribution function is m*exp(-mx) so the CDF is
48+
// p = 1 - exp(-mx), so
49+
// q = 1 - p = exp(-mx)
50+
// log_e(q) = -mx
51+
// -log_e(q)/m = x
52+
// log_2(q) * (-log_e(2) * 1/m) = x
53+
// In the code, q is actually in the range 1 to 2**26, hence the -26 below
54+
private ulong GetNextThreshold()
55+
{
56+
_random = GetNextRandom(_random);
57+
58+
// Take the top 26 bits as the random number
59+
// (This plus a 1<<58 sampling bound gives a max possible step of
60+
// 5194297183973780480 bytes. In this case,
61+
// for sample_parameter = 1<<19, max possible step is
62+
// 9448372 bytes (24 bits).
63+
64+
// 48 is the number of bits in prng
65+
// The uint32_t cast is to prevent a (hard-to-reproduce) NAN
66+
// under piii debug for some binaries.
67+
double q = (uint)(_random >> (48 - 26)) + 1.0;
68+
// Put the computed p-value through the CDF of a geometric.
69+
// For faster performance (save ~1/20th exec time), replace
70+
// min(0.0, FastLog2(q) - 26) by (Fastlog2(q) - 26.000705)
71+
// The value 26.000705 is used rather than 26 to compensate
72+
// for inaccuracies in FastLog2 which otherwise result in a
73+
// negative answer.
74+
double log_val = (Log2(q) - 26);
75+
if (log_val > 0.0)
76+
{
77+
log_val = 0.0;
78+
}
79+
80+
double result = (log_val * (MinusLog2 * (MeanSamplingSize))) + 1;
81+
ulong interval = (ulong)result;
82+
_threshold = interval;
83+
return _threshold;
84+
}
85+
86+
// Returns the next prng value.
87+
// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48
88+
// This is the lrand64 generator.
89+
private ulong GetNextRandom(ulong random)
90+
{
91+
return ((PrngMult * random) + PrngAdd) & PrngModMask;
92+
}
93+
94+
private double Log2(double d)
95+
{
96+
return Math.Log2(d);
97+
}
98+
}
99+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// <copyright file="PoissonSampler.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
4+
// </copyright>
5+
6+
namespace AllocSimulator
7+
{
8+
public class PoissonSampler : ISampler
9+
{
10+
private Random _randomizer = new Random(DateTime.Now.Millisecond);
11+
private ulong _totalAllocatedAmount;
12+
private ulong _threshold; // number of bytes until the next sample
13+
private float _meanPoisson;
14+
15+
public PoissonSampler(int meanPoisson)
16+
{
17+
_meanPoisson = meanPoisson * 1024;
18+
_threshold = GetNextThreshold();
19+
}
20+
21+
public bool ShouldSample(long size)
22+
{
23+
_totalAllocatedAmount += (ulong)size;
24+
var shouldSample = _totalAllocatedAmount > _threshold;
25+
26+
if (shouldSample)
27+
{
28+
_totalAllocatedAmount = 0;
29+
_threshold = GetNextThreshold();
30+
}
31+
32+
return shouldSample;
33+
}
34+
35+
public ulong GetNextThreshold()
36+
{
37+
var q = _randomizer.NextDouble();
38+
39+
ulong next = (ulong)((-Math.Log(1 - q)) * _meanPoisson) + 1;
40+
return next;
41+
}
42+
}
43+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// <copyright file="PoissonUpscaler.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
4+
// </copyright>
5+
6+
namespace AllocSimulator
7+
{
8+
public class PoissonUpscaler : UpscalerBase
9+
{
10+
private float _meanPoisson;
11+
12+
public PoissonUpscaler(int meanPoisson)
13+
{
14+
_meanPoisson = meanPoisson * 1024;
15+
}
16+
17+
protected override void OnUpscale(AllocInfo sampled, ref AllocInfo upscaled)
18+
{
19+
var averageSize = (double)sampled.Size / (double)sampled.Count;
20+
var scale = 1 / (1 - Math.Exp(-averageSize / _meanPoisson));
21+
22+
upscaled.Size = (long)((float)sampled.Size * scale);
23+
upscaled.Count = (int)((float)sampled.Count * scale);
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)