diff --git a/csharp/Platform.Collections.Benchmarks/BitStringBenchmarks.cs b/csharp/Platform.Collections.Benchmarks/BitStringBenchmarks.cs index 67649f9b..105cd2ce 100644 --- a/csharp/Platform.Collections.Benchmarks/BitStringBenchmarks.cs +++ b/csharp/Platform.Collections.Benchmarks/BitStringBenchmarks.cs @@ -23,6 +23,17 @@ public void Setup() } } + [Benchmark] + public BitString CreateWithRandomBitsOld() + { + var bitString = new BitString(N); + bitString.SetRandomBits(); + return bitString; + } + + [Benchmark] + public BitString CreateWithRandomBitsNew() => BitStringExtensions.CreateWithRandomBits(N); + [Benchmark] public BitString Not() => new BitString(_left).Not(); diff --git a/csharp/Platform.Collections.Tests/BitStringTests.cs b/csharp/Platform.Collections.Tests/BitStringTests.cs index 988deca8..5655d532 100644 --- a/csharp/Platform.Collections.Tests/BitStringTests.cs +++ b/csharp/Platform.Collections.Tests/BitStringTests.cs @@ -23,6 +23,40 @@ public static void BitGetSetTest() } } + [Fact] + public static void BitStringArrayConstructorTest() + { + const int n = 128; + var array = new long[] { unchecked((long)0x1234567890ABCDEF), unchecked((long)0xFEDCBA0987654321) }; + var bitString = new BitString(array, n); + + var normalBitString = new BitString(n); + for (var i = 0L; i < BitString.GetWordsCountFromIndex(n); i++) + { + for (var j = 0; j < 64 && (i * 64 + j) < n; j++) + { + var bitIndex = i * 64 + j; + var expectedValue = (array[i] & (1L << j)) != 0; + normalBitString.Set(bitIndex, expectedValue); + Assert.Equal(expectedValue, bitString.Get(bitIndex)); + } + } + + Assert.Equal(normalBitString.CountSetBits(), bitString.CountSetBits()); + } + + [Fact] + public static void CreateWithRandomBitsTest() + { + const int n = 1000; + var bitString1 = BitStringExtensions.CreateWithRandomBits(n); + var bitString2 = BitStringExtensions.CreateWithRandomBits(n); + + Assert.Equal(n, bitString1.Length); + Assert.Equal(n, bitString2.Length); + Assert.False(bitString1.Equals(bitString2)); + } + [Fact] public static void BitVectorNotTest() { diff --git a/csharp/Platform.Collections.Tests/PerformanceComparisonTest.cs b/csharp/Platform.Collections.Tests/PerformanceComparisonTest.cs new file mode 100644 index 00000000..89b9ad88 --- /dev/null +++ b/csharp/Platform.Collections.Tests/PerformanceComparisonTest.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; +using Xunit; +using Xunit.Abstractions; + +namespace Platform.Collections.Tests +{ + public class PerformanceComparisonTest + { + private readonly ITestOutputHelper _output; + + public PerformanceComparisonTest(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void BitStringInitializationPerformanceComparison() + { + const int iterations = 100; + const long bitStringLength = 10000; + + _output.WriteLine($"Testing BitString initialization performance with {iterations} iterations for length {bitStringLength}"); + + // Test old method (SetRandomBits) + var sw = Stopwatch.StartNew(); + for (int i = 0; i < iterations; i++) + { + var bitString = new BitString(bitStringLength); + bitString.SetRandomBits(); + } + sw.Stop(); + var oldMethodTime = sw.ElapsedMilliseconds; + _output.WriteLine($"Old method (SetRandomBits): {oldMethodTime} ms"); + + // Test new method (array constructor) + sw.Restart(); + for (int i = 0; i < iterations; i++) + { + var bitString = BitStringExtensions.CreateWithRandomBits(bitStringLength); + } + sw.Stop(); + var newMethodTime = sw.ElapsedMilliseconds; + _output.WriteLine($"New method (CreateWithRandomBits): {newMethodTime} ms"); + + if (oldMethodTime > 0 && newMethodTime > 0) + { + var improvement = (double)oldMethodTime / newMethodTime; + _output.WriteLine($"Performance improvement: {improvement:F2}x faster"); + + // Assert that the new method is faster or at least not significantly slower + Assert.True(newMethodTime <= oldMethodTime, $"New method should be faster or equal. Old: {oldMethodTime}ms, New: {newMethodTime}ms"); + } + else + { + _output.WriteLine("Performance test completed, but times were too small to measure accurately"); + } + } + } +} \ No newline at end of file diff --git a/csharp/Platform.Collections/BitString.cs b/csharp/Platform.Collections/BitString.cs index 4b0b36ac..33d4e116 100644 --- a/csharp/Platform.Collections/BitString.cs +++ b/csharp/Platform.Collections/BitString.cs @@ -193,6 +193,36 @@ public BitString(long length, bool defaultValue) } } + /// + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// An array of integers to initialize the bit string with. + /// + /// + /// + /// A length. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BitString(long[] array, long length) + { + Ensure.Always.ArgumentNotNull(array, nameof(array)); + Ensure.Always.ArgumentInRange(length, GetValidLengthRange(), nameof(length)); + var wordsCount = GetWordsCountFromIndex(length); + if (array.LongLength < wordsCount) + { + throw new ArgumentException($"Array must have at least {wordsCount} elements for length {length}.", nameof(array)); + } + _length = length; + _array = new long[wordsCount]; + Array.Copy(array, _array, wordsCount); + SetBordersFromArray(); + } + #endregion /// @@ -1533,6 +1563,33 @@ private void EnsureBitStringHasTheSameSize(BitString other, string argumentName) [MethodImpl(MethodImplOptions.AggressiveInlining)] private void MarkBordersAsAllBitsSet() => SetBorders(0, _array.LongLength - 1); [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetBordersFromArray() + { + long minPositiveWord = _array.LongLength - 1; + long maxPositiveWord = 0; + var hasPositiveBits = false; + for (var i = 0L; i < _array.LongLength; i++) + { + if (_array[i] != 0) + { + if (!hasPositiveBits) + { + minPositiveWord = i; + hasPositiveBits = true; + } + maxPositiveWord = i; + } + } + if (!hasPositiveBits) + { + MarkBordersAsAllBitsReset(); + } + else + { + SetBorders(minPositiveWord, maxPositiveWord); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void GetBorders(out long from, out long to) { from = _minPositiveWord; diff --git a/csharp/Platform.Collections/BitStringExtensions.cs b/csharp/Platform.Collections/BitStringExtensions.cs index 85c92e62..be7354a8 100644 --- a/csharp/Platform.Collections/BitStringExtensions.cs +++ b/csharp/Platform.Collections/BitStringExtensions.cs @@ -32,5 +32,31 @@ public static void SetRandomBits(this BitString @string) @string.Set(i, value); } } + + /// + /// + /// Creates a new BitString with random bits using the efficient constructor. + /// + /// + /// + /// + /// The length of the bit string. + /// + /// + /// + /// A new BitString with random bits. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BitString CreateWithRandomBits(long length) + { + var wordsCount = BitString.GetWordsCountFromIndex(length); + var randomArray = new long[wordsCount]; + for (var i = 0L; i < wordsCount; i++) + { + randomArray[i] = RandomHelpers.Default.NextInt64(); + } + return new BitString(randomArray, length); + } } }