Exponential Bucket Histogram - part 5 (#3482)
This commit is contained in:
parent
a55e3391cd
commit
6f2b1a0b34
|
|
@ -15,6 +15,7 @@
|
|||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace OpenTelemetry.Internal;
|
||||
|
|
@ -96,6 +97,36 @@ internal static class MathHelper
|
|||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int PositiveModulo32(int value, int divisor)
|
||||
{
|
||||
Debug.Assert(divisor > 0, $"{nameof(divisor)} must be a positive integer.");
|
||||
|
||||
value %= divisor;
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
value += divisor;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long PositiveModulo64(long value, long divisor)
|
||||
{
|
||||
Debug.Assert(divisor > 0, $"{nameof(divisor)} must be a positive integer.");
|
||||
|
||||
value %= divisor;
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
value += divisor;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsFinite(double value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using OpenTelemetry.Internal;
|
||||
|
||||
|
|
@ -40,6 +41,11 @@ internal sealed class CircularBufferBuckets
|
|||
/// </summary>
|
||||
public int Capacity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset of the start index for the <see cref="CircularBufferBuckets"/>.
|
||||
/// </summary>
|
||||
public int Offset => this.begin;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the <see cref="CircularBufferBuckets"/>.
|
||||
/// </summary>
|
||||
|
|
@ -60,20 +66,20 @@ internal sealed class CircularBufferBuckets
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to increment the value of <c>Bucket[index]</c>.
|
||||
/// Attempts to increment the value of <c>Bucket[index]</c> by <c>value</c>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the bucket.</param>
|
||||
/// <param name="value">The increment.</param>
|
||||
/// <returns>
|
||||
/// Returns <c>0</c> if the increment attempt succeeded;
|
||||
/// Returns a positive integer <c>Math.Ceiling(log_2(X))</c> if the
|
||||
/// underlying buffer is running out of capacity, and the buffer has to
|
||||
/// increase to <c>X * Capacity</c> at minimum.
|
||||
/// Returns a positive integer indicating the minimum scale reduction level
|
||||
/// if the increment attempt failed.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// The "index" value can be positive, zero or negative.
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int TryIncrement(int index)
|
||||
public int TryIncrement(int index, long value = 1)
|
||||
{
|
||||
var capacity = this.Capacity;
|
||||
|
||||
|
|
@ -107,7 +113,7 @@ internal sealed class CircularBufferBuckets
|
|||
this.begin = index;
|
||||
}
|
||||
|
||||
this.trait[this.ModuloIndex(index)] += 1;
|
||||
this.trait[this.ModuloIndex(index)] += value;
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -130,16 +136,141 @@ internal sealed class CircularBufferBuckets
|
|||
}
|
||||
}
|
||||
|
||||
public void ScaleDown(int level = 1)
|
||||
{
|
||||
Debug.Assert(level > 0, "The scale down level must be a positive integer.");
|
||||
|
||||
if (this.trait == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 0 <= offset < capacity <= 2147483647
|
||||
uint capacity = (uint)this.Capacity;
|
||||
var offset = (uint)this.ModuloIndex(this.begin);
|
||||
|
||||
var currentBegin = this.begin;
|
||||
var currentEnd = this.end;
|
||||
|
||||
for (int i = 0; i < level; i++)
|
||||
{
|
||||
var newBegin = currentBegin >> 1;
|
||||
var newEnd = currentEnd >> 1;
|
||||
|
||||
if (currentBegin != currentEnd)
|
||||
{
|
||||
if (currentBegin % 2 == 0)
|
||||
{
|
||||
ScaleDownInternal(this.trait, offset, currentBegin, currentEnd, capacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentBegin++;
|
||||
|
||||
if (currentBegin != currentEnd)
|
||||
{
|
||||
ScaleDownInternal(this.trait, offset + 1, currentBegin, currentEnd, capacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentBegin = newBegin;
|
||||
currentEnd = newEnd;
|
||||
}
|
||||
|
||||
this.begin = currentBegin;
|
||||
this.end = currentEnd;
|
||||
|
||||
if (capacity > 1)
|
||||
{
|
||||
AdjustPosition(this.trait, offset, (uint)this.ModuloIndex(currentBegin), (uint)(currentEnd - currentBegin + 1), capacity);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void ScaleDownInternal(long[] array, uint offset, int begin, int end, uint capacity)
|
||||
{
|
||||
for (var index = begin + 1; index < end; index++)
|
||||
{
|
||||
Consolidate(array, (offset + (uint)(index - begin)) % capacity, (offset + (uint)((index >> 1) - (begin >> 1))) % capacity);
|
||||
}
|
||||
|
||||
Consolidate(array, (offset + (uint)(end - begin)) % capacity, (offset + (uint)((end >> 1) - (begin >> 1))) % capacity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void AdjustPosition(long[] array, uint src, uint dst, uint size, uint capacity)
|
||||
{
|
||||
var advancement = (dst + capacity - src) % capacity;
|
||||
|
||||
if (advancement == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (size - 1 == advancement && advancement << 1 == capacity)
|
||||
{
|
||||
Exchange(array, src++, dst++);
|
||||
size -= 2;
|
||||
}
|
||||
else if (advancement < size)
|
||||
{
|
||||
src = src + size - 1;
|
||||
dst = dst + size - 1;
|
||||
|
||||
while (size-- != 0)
|
||||
{
|
||||
Move(array, src-- % capacity, dst-- % capacity);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (size-- != 0)
|
||||
{
|
||||
Move(array, src++ % capacity, dst++ % capacity);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void Consolidate(long[] array, uint src, uint dst)
|
||||
{
|
||||
array[dst] += array[src];
|
||||
array[src] = 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void Exchange(long[] array, uint src, uint dst)
|
||||
{
|
||||
var value = array[dst];
|
||||
array[dst] = array[src];
|
||||
array[src] = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static void Move(long[] array, uint src, uint dst)
|
||||
{
|
||||
array[dst] = array[src];
|
||||
array[src] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(CircularBufferBuckets)
|
||||
+ "{"
|
||||
+ nameof(this.Capacity) + "=" + this.Capacity + ", "
|
||||
+ nameof(this.Size) + "=" + this.Size + ", "
|
||||
+ nameof(this.begin) + "=" + this.begin + ", "
|
||||
+ nameof(this.end) + "=" + this.end + ", "
|
||||
+ (this.trait == null ? "null" : "{" + string.Join(", ", this.trait) + "}")
|
||||
+ "}";
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private int ModuloIndex(int value)
|
||||
{
|
||||
value %= this.Capacity;
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
value += this.Capacity;
|
||||
}
|
||||
|
||||
return value;
|
||||
return MathHelper.PositiveModulo32(value, this.Capacity);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,11 +124,29 @@ internal class ExponentialBucketHistogram
|
|||
|
||||
if (c > 0)
|
||||
{
|
||||
this.PositiveBuckets.TryIncrement(this.MapToIndex(value));
|
||||
var index = this.MapToIndex(value);
|
||||
var n = this.PositiveBuckets.TryIncrement(index);
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
this.PositiveBuckets.ScaleDown(n);
|
||||
this.NegativeBuckets.ScaleDown(n);
|
||||
n = this.PositiveBuckets.TryIncrement(index);
|
||||
Debug.Assert(n == 0, "Increment should always succeed after scale down.");
|
||||
}
|
||||
}
|
||||
else if (c < 0)
|
||||
{
|
||||
this.NegativeBuckets.TryIncrement(this.MapToIndex(-value));
|
||||
var index = this.MapToIndex(-value);
|
||||
var n = this.NegativeBuckets.TryIncrement(index);
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
this.PositiveBuckets.ScaleDown(n);
|
||||
this.NegativeBuckets.ScaleDown(n);
|
||||
n = this.NegativeBuckets.TryIncrement(index);
|
||||
Debug.Assert(n == 0, "Increment should always succeed after scale down.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace OpenTelemetry.Metrics.Tests;
|
|||
public class CircularBufferBucketsTest
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor()
|
||||
public void ConstructorThrowsOnInvalidCapacity()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new CircularBufferBuckets(0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new CircularBufferBuckets(-1));
|
||||
|
|
@ -73,6 +73,9 @@ public class CircularBufferBucketsTest
|
|||
Assert.Equal(0, buckets.TryIncrement(100));
|
||||
Assert.Equal(0, buckets.TryIncrement(104));
|
||||
|
||||
Assert.Equal(100, buckets.Offset);
|
||||
Assert.Equal(5, buckets.Size);
|
||||
|
||||
Assert.Equal(1, buckets.TryIncrement(99));
|
||||
Assert.Equal(1, buckets.TryIncrement(105));
|
||||
}
|
||||
|
|
@ -88,6 +91,9 @@ public class CircularBufferBucketsTest
|
|||
Assert.Equal(0, buckets.TryIncrement(1));
|
||||
Assert.Equal(0, buckets.TryIncrement(-1));
|
||||
|
||||
Assert.Equal(-2, buckets.Offset);
|
||||
Assert.Equal(5, buckets.Size);
|
||||
|
||||
Assert.Equal(1, buckets.TryIncrement(3));
|
||||
Assert.Equal(1, buckets.TryIncrement(-3));
|
||||
}
|
||||
|
|
@ -98,6 +104,10 @@ public class CircularBufferBucketsTest
|
|||
var buckets = new CircularBufferBuckets(1);
|
||||
|
||||
Assert.Equal(0, buckets.TryIncrement(int.MaxValue));
|
||||
|
||||
Assert.Equal(int.MaxValue, buckets.Offset);
|
||||
Assert.Equal(1, buckets.Size);
|
||||
|
||||
Assert.Equal(31, buckets.TryIncrement(1));
|
||||
Assert.Equal(31, buckets.TryIncrement(0));
|
||||
Assert.Equal(32, buckets.TryIncrement(-1));
|
||||
|
|
@ -126,10 +136,280 @@ public class CircularBufferBucketsTest
|
|||
buckets.TryIncrement(-1);
|
||||
buckets.TryIncrement(-1);
|
||||
|
||||
Assert.Equal(-2, buckets.Offset);
|
||||
|
||||
Assert.Equal(1, buckets[-2]);
|
||||
Assert.Equal(2, buckets[-1]);
|
||||
Assert.Equal(3, buckets[0]);
|
||||
Assert.Equal(4, buckets[1]);
|
||||
Assert.Equal(5, buckets[2]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScaleDownCapacity1()
|
||||
{
|
||||
var buckets = new CircularBufferBuckets(1);
|
||||
|
||||
buckets.ScaleDown(1);
|
||||
buckets.ScaleDown(2);
|
||||
buckets.ScaleDown(3);
|
||||
buckets.ScaleDown(4);
|
||||
|
||||
buckets.TryIncrement(0);
|
||||
|
||||
Assert.Equal(0, buckets.Offset);
|
||||
Assert.Equal(1, buckets.Size);
|
||||
Assert.Equal(1, buckets[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScaleDownIntMaxValue()
|
||||
{
|
||||
var buckets = new CircularBufferBuckets(1);
|
||||
|
||||
buckets.TryIncrement(int.MaxValue);
|
||||
|
||||
Assert.Equal(int.MaxValue, buckets.Offset);
|
||||
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(0x3FFFFFFF, buckets.Offset);
|
||||
Assert.Equal(1, buckets[0x3FFFFFFF]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScaleDownIntMinValue()
|
||||
{
|
||||
var buckets = new CircularBufferBuckets(1);
|
||||
|
||||
buckets.TryIncrement(int.MinValue);
|
||||
|
||||
Assert.Equal(int.MinValue, buckets.Offset);
|
||||
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(-0x40000000, buckets.Offset);
|
||||
Assert.Equal(1, buckets[-0x40000000]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScaleDownCapacity2()
|
||||
{
|
||||
var buckets = new CircularBufferBuckets(2);
|
||||
|
||||
buckets.TryIncrement(int.MinValue, 2);
|
||||
buckets.TryIncrement(int.MinValue + 1);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(1, buckets.Size);
|
||||
Assert.Equal(3, buckets[buckets.Offset]);
|
||||
|
||||
buckets = new CircularBufferBuckets(2);
|
||||
|
||||
buckets.TryIncrement(int.MaxValue - 1, 2);
|
||||
buckets.TryIncrement(int.MaxValue);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(1, buckets.Size);
|
||||
Assert.Equal(3, buckets[buckets.Offset]);
|
||||
Assert.Equal(0, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(2);
|
||||
|
||||
buckets.TryIncrement(int.MaxValue - 2, 2);
|
||||
buckets.TryIncrement(int.MaxValue - 1);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(2, buckets[buckets.Offset]);
|
||||
Assert.Equal(1, buckets[buckets.Offset + 1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScaleDownCapacity3()
|
||||
{
|
||||
var buckets = new CircularBufferBuckets(3);
|
||||
|
||||
buckets.TryIncrement(0, 2);
|
||||
buckets.TryIncrement(1, 4);
|
||||
buckets.TryIncrement(2, 8);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(0, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(6, buckets[buckets.Offset]);
|
||||
Assert.Equal(8, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(3);
|
||||
|
||||
buckets.TryIncrement(1, 2);
|
||||
buckets.TryIncrement(2, 4);
|
||||
buckets.TryIncrement(3, 8);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(0, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(2, buckets[buckets.Offset]);
|
||||
Assert.Equal(12, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(3);
|
||||
|
||||
buckets.TryIncrement(2, 2);
|
||||
buckets.TryIncrement(3, 4);
|
||||
buckets.TryIncrement(4, 8);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(1, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(6, buckets[buckets.Offset]);
|
||||
Assert.Equal(8, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(3);
|
||||
|
||||
buckets.TryIncrement(3, 2);
|
||||
buckets.TryIncrement(4, 4);
|
||||
buckets.TryIncrement(5, 8);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(1, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(2, buckets[buckets.Offset]);
|
||||
Assert.Equal(12, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(3);
|
||||
|
||||
buckets.TryIncrement(4, 2);
|
||||
buckets.TryIncrement(5, 4);
|
||||
buckets.TryIncrement(6, 8);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(2, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(6, buckets[buckets.Offset]);
|
||||
Assert.Equal(8, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(3);
|
||||
|
||||
buckets.TryIncrement(5, 2);
|
||||
buckets.TryIncrement(6, 4);
|
||||
buckets.TryIncrement(7, 8);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(2, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(2, buckets[buckets.Offset]);
|
||||
Assert.Equal(12, buckets[buckets.Offset + 1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScaleDownCapacity4()
|
||||
{
|
||||
var buckets = new CircularBufferBuckets(4);
|
||||
|
||||
buckets.TryIncrement(0, 2);
|
||||
buckets.TryIncrement(1, 4);
|
||||
buckets.TryIncrement(2, 8);
|
||||
buckets.TryIncrement(2, 16);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(0, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(6, buckets[buckets.Offset]);
|
||||
Assert.Equal(24, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(4);
|
||||
|
||||
buckets.TryIncrement(1, 2);
|
||||
buckets.TryIncrement(2, 4);
|
||||
buckets.TryIncrement(3, 8);
|
||||
buckets.TryIncrement(4, 16);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(0, buckets.Offset);
|
||||
Assert.Equal(3, buckets.Size);
|
||||
Assert.Equal(2, buckets[buckets.Offset]);
|
||||
Assert.Equal(12, buckets[buckets.Offset + 1]);
|
||||
Assert.Equal(16, buckets[buckets.Offset + 2]);
|
||||
|
||||
buckets = new CircularBufferBuckets(4);
|
||||
|
||||
buckets.TryIncrement(2, 2);
|
||||
buckets.TryIncrement(3, 4);
|
||||
buckets.TryIncrement(4, 8);
|
||||
buckets.TryIncrement(5, 16);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(1, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(6, buckets[buckets.Offset]);
|
||||
Assert.Equal(24, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(4);
|
||||
|
||||
buckets.TryIncrement(3, 2);
|
||||
buckets.TryIncrement(4, 4);
|
||||
buckets.TryIncrement(5, 8);
|
||||
buckets.TryIncrement(6, 16);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(1, buckets.Offset);
|
||||
Assert.Equal(3, buckets.Size);
|
||||
Assert.Equal(2, buckets[buckets.Offset]);
|
||||
Assert.Equal(12, buckets[buckets.Offset + 1]);
|
||||
Assert.Equal(16, buckets[buckets.Offset + 2]);
|
||||
|
||||
buckets = new CircularBufferBuckets(4);
|
||||
|
||||
buckets.TryIncrement(4, 2);
|
||||
buckets.TryIncrement(5, 4);
|
||||
buckets.TryIncrement(6, 8);
|
||||
buckets.TryIncrement(7, 16);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(2, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(6, buckets[buckets.Offset]);
|
||||
Assert.Equal(24, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(4);
|
||||
|
||||
buckets.TryIncrement(5, 2);
|
||||
buckets.TryIncrement(6, 4);
|
||||
buckets.TryIncrement(7, 8);
|
||||
buckets.TryIncrement(8, 16);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(2, buckets.Offset);
|
||||
Assert.Equal(3, buckets.Size);
|
||||
Assert.Equal(2, buckets[buckets.Offset]);
|
||||
Assert.Equal(12, buckets[buckets.Offset + 1]);
|
||||
Assert.Equal(16, buckets[buckets.Offset + 2]);
|
||||
|
||||
buckets = new CircularBufferBuckets(4);
|
||||
|
||||
buckets.TryIncrement(6, 2);
|
||||
buckets.TryIncrement(7, 4);
|
||||
buckets.TryIncrement(8, 8);
|
||||
buckets.TryIncrement(9, 16);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(3, buckets.Offset);
|
||||
Assert.Equal(2, buckets.Size);
|
||||
Assert.Equal(6, buckets[buckets.Offset]);
|
||||
Assert.Equal(24, buckets[buckets.Offset + 1]);
|
||||
|
||||
buckets = new CircularBufferBuckets(4);
|
||||
|
||||
buckets.TryIncrement(7, 2);
|
||||
buckets.TryIncrement(8, 4);
|
||||
buckets.TryIncrement(9, 8);
|
||||
buckets.TryIncrement(10, 16);
|
||||
buckets.ScaleDown(1);
|
||||
|
||||
Assert.Equal(3, buckets.Offset);
|
||||
Assert.Equal(3, buckets.Size);
|
||||
Assert.Equal(2, buckets[buckets.Offset]);
|
||||
Assert.Equal(12, buckets[buckets.Offset + 1]);
|
||||
Assert.Equal(16, buckets[buckets.Offset + 2]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue