未验证 提交 ffcef4af 编写于 作者: H Huo Yaoyuan 提交者: GitHub

Expose RoundUpToPowerOf2 (#53992)

* 32bit

* 64bit

* Expose in public api

* Unit test

* Add more tests

* Use in test

* Apply suggestions from code review
Co-authored-by: NStephen Toub <stoub@microsoft.com>
Co-authored-by: NStephen Toub <stoub@microsoft.com>
上级 146b3b16
......@@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using Xunit;
namespace System.Collections.Generic.Tests
......@@ -205,29 +206,7 @@ private static int CalculateExpectedCapacity(int count)
// Then allocate arrays of size 4, 8, 16, etc.
count = Math.Max(count, 4);
return NextPowerOfTwo(count);
}
private static int NextPowerOfTwo(int value)
{
// Taken from https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
Debug.Assert(value >= 0);
// If the number is already a power of 2, we want to round to itself.
value--;
// Propogate 1-bits right: if the highest bit set is @ position n,
// then all of the bits to the right of position n will become set.
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
// This yields a number of the form 2^N - 1.
// Add 1 to get a power of 2 with the bit set @ position n + 1.
return value + 1;
return (int)BitOperations.RoundUpToPowerOf2((uint)count);
}
}
......
......@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;
......@@ -278,23 +279,11 @@ public static void CanRentManySizedBuffers(ArrayPool<byte> pool)
for (int i = 1; i < 10000; i++)
{
byte[] buffer = pool.Rent(i);
Assert.Equal(i <= 16 ? 16 : RoundUpToPowerOf2(i), buffer.Length);
Assert.Equal(i <= 16 ? 16 : (int)BitOperations.RoundUpToPowerOf2((uint)i), buffer.Length);
pool.Return(buffer);
}
}
private static int RoundUpToPowerOf2(int i)
{
// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--i;
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
return i + 1;
}
[Theory]
[InlineData(1, 16)]
[InlineData(15, 16)]
......
......@@ -71,18 +71,55 @@ public static class BitOperations
[CLSCompliant(false)]
public static bool IsPow2(ulong value) => (value & (value - 1)) == 0 && value != 0;
/// <summary>Round the given integral value up to a power of 2.</summary>
/// <summary>
/// Round the given integral value up to a power of 2.
/// </summary>
/// <param name="value">The value.</param>
internal static uint RoundUpToPowerOf2(uint value)
/// <returns>
/// The smallest power of 2 which is greater than or equal to <paramref name="value"/>.
/// If <paramref name="value"/> is 0 or the result overflows, returns 0.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CLSCompliant(false)]
public static uint RoundUpToPowerOf2(uint value)
{
// TODO: https://github.com/dotnet/runtime/issues/43135
// When this is exposed publicly, decide on the behavior for the boundary cases...
// the accelerated and fallback paths differ.
Debug.Assert(value > 0 && value <= (uint.MaxValue / 2) + 1);
if (Lzcnt.IsSupported || ArmBase.IsSupported || X86Base.IsSupported)
{
return 1u << (32 - LeadingZeroCount(value - 1));
#if TARGET_64BIT
return (uint)(0x1_0000_0000ul >> LeadingZeroCount(value - 1));
#else
int shift = 32 - LeadingZeroCount(value - 1);
return (1u ^ (uint)(shift >> 5)) << shift;
#endif
}
// Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--value;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return value + 1;
}
/// <summary>
/// Round the given integral value up to a power of 2.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// The smallest power of 2 which is greater than or equal to <paramref name="value"/>.
/// If <paramref name="value"/> is 0 or the result overflows, returns 0.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CLSCompliant(false)]
public static ulong RoundUpToPowerOf2(ulong value)
{
if (Lzcnt.X64.IsSupported || ArmBase.Arm64.IsSupported)
{
int shift = 64 - LeadingZeroCount(value - 1);
return (1ul ^ (ulong)(shift >> 6)) << shift;
}
// Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
......@@ -92,6 +129,7 @@ internal static uint RoundUpToPowerOf2(uint value)
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value |= value >> 32;
return value + 1;
}
......
......@@ -388,5 +388,53 @@ public static void BitOps_RotateRight_ulong()
Assert.Equal(value, BitOperations.RotateRight(value, int.MinValue)); // % 64 = 0
Assert.Equal(BitOperations.RotateLeft(value, 63), BitOperations.RotateRight(value, int.MaxValue)); // % 64 = 63
}
[Theory]
[InlineData(0u, 0u)]
[InlineData(1u, 1u)]
[InlineData(2u, 2u)]
[InlineData(0x0096u, 0x0100u)]
[InlineData(0x05CDu, 0x0800u)]
[InlineData(0x0932u, 0x1000u)]
[InlineData(0x0004_C911u, 0x0008_0000u)]
[InlineData(0x00E0_A2E2u, 0x0100_0000u)]
[InlineData(0x0988_0713u, 0x1000_0000u)]
[InlineData(0x30A4_9649u, 0x4000_0000u)]
[InlineData(0x7FFF_FFFFu, 0x8000_0000u)]
[InlineData(0x8000_0000u, 0x8000_0000u)]
[InlineData(0x8000_0001u, 0ul)]
[InlineData(0xFFFF_FFFFu, 0ul)]
public static void BitOps_RoundUpToPow2_uint(uint value, uint expected)
{
Assert.Equal(expected, BitOperations.RoundUpToPowerOf2(value));
}
[Theory]
[InlineData(0ul, 0ul)]
[InlineData(1ul, 1ul)]
[InlineData(2ul, 2ul)]
[InlineData(0x0096ul, 0x0100ul)]
[InlineData(0x05cdul, 0x0800ul)]
[InlineData(0x0932ul, 0x1000ul)]
[InlineData(0x0004_c911ul, 0x0008_0000ul)]
[InlineData(0x00e0_a2b2ul, 0x0100_0000ul)]
[InlineData(0x0988_0713ul, 0x1000_0000ul)]
[InlineData(0x30a4_9649ul, 0x4000_0000ul)]
[InlineData(0x7FFF_FFFFul, 0x8000_0000ul)]
[InlineData(0x8000_0000ul, 0x8000_0000ul)]
[InlineData(0x8000_0001ul, 0x1_0000_0000ul)]
[InlineData(0xFFFF_FFFFul, 0x1_0000_0000ul)]
[InlineData(0x0000_0003_343B_0D81ul, 0x0000_0004_0000_0000ul)]
[InlineData(0x0000_0D87_5EE2_8F19ul, 0x0000_1000_0000_0000ul)]
[InlineData(0x0006_2A08_4A7A_3A2Dul, 0x0008_0000_0000_0000ul)]
[InlineData(0x0101_BF76_4398_F791ul, 0x0200_0000_0000_0000ul)]
[InlineData(0x7FFF_FFFF_FFFF_FFFFul, 0x8000_0000_0000_0000ul)]
[InlineData(0x8000_0000_0000_0000ul, 0x8000_0000_0000_0000ul)]
[InlineData(0x8000_0000_0000_0001ul, 0ul)]
[InlineData(0xFFFF_FFFF_FFFF_FFFFul, 0ul)]
public static void BitOps_RoundUpToPow2_ulong(ulong value, ulong expected)
{
Assert.Equal(expected, BitOperations.RoundUpToPowerOf2(value));
}
}
}
......@@ -8196,6 +8196,10 @@ public static partial class BitOperations
public static uint RotateRight(uint value, int offset) { throw null; }
[System.CLSCompliantAttribute(false)]
public static ulong RotateRight(ulong value, int offset) { throw null; }
[System.CLSCompliantAttribute(false)]
public static uint RoundUpToPowerOf2(uint value) { throw null; }
[System.CLSCompliantAttribute(false)]
public static ulong RoundUpToPowerOf2(ulong value) { throw null; }
public static int TrailingZeroCount(int value) { throw null; }
public static int TrailingZeroCount(long value) { throw null; }
[System.CLSCompliantAttribute(false)]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册