From a3225f8d2d04f5ec834447408f9762e2392c21bc Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 14 Sep 2023 02:34:38 +0200 Subject: [PATCH] `BitSet.Exclusive` is now vectorized. --- src/Arch.Tests/BitSetTest.cs | 48 ++++++++++++++++++++++++++++-- src/Arch/Core/Utils/BitSet.cs | 56 +++++++++++++++++++++++++++-------- 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/Arch.Tests/BitSetTest.cs b/src/Arch.Tests/BitSetTest.cs index 6e500933..01f99008 100644 --- a/src/Arch.Tests/BitSetTest.cs +++ b/src/Arch.Tests/BitSetTest.cs @@ -152,16 +152,58 @@ public void BitsetNone(int[] values, int multiplier) That(allResult, Is.EqualTo(false)); } + /// + /// Checks exclusive. + /// The values being set or cleared. + /// The multiplier for the passed values. Mainly for vectorization-testing to increase the set bits. + /// + [Test] + [TestCase(new []{5,6,25}, 1)] + [TestCase(new []{5,6,25}, 100)] + public void BitsetExclusive(int[] values, int multiplier) + { + var bitSet1 = new BitSet(); + bitSet1.SetBit(values[0] * multiplier); + bitSet1.SetBit(values[1] * multiplier); + var bitSet2 = new BitSet(); + bitSet2.SetBit(values[0] * multiplier); + bitSet2.SetBit(values[1] * multiplier); + + // Both are totally equal + var exclusive = bitSet2.Exclusive(bitSet1); + That(exclusive, Is.EqualTo(true)); + + // Bitset2 is not equal anymore + bitSet2.SetBit(values[2] * multiplier); + exclusive = bitSet2.Exclusive(bitSet1); + That(exclusive, Is.EqualTo(false)); + + // Bitset2 is back to default, but bitset1 is now different -> both differ, should be false + bitSet2.ClearBit(values[2] * multiplier); + bitSet1.SetBit(values[2] * multiplier); + exclusive = bitSet2.Exclusive(bitSet1); + That(exclusive, Is.EqualTo(false)); + + // Bitset2 is now the same as bitset 1 -> should be true + bitSet2.SetBit(values[2] * multiplier); + exclusive = bitSet2.Exclusive(bitSet1); + That(exclusive, Is.EqualTo(true)); + } + /// /// Checks whether different sized 's work correctly. + /// The values being set or cleared. + /// The multiplier for the passed values. Mainly for vectorization-testing to increase the set bits. /// [Test] - public void AllWithDifferentLengthBitSet() + [TestCase(new []{5,33}, 1)] + [TestCase(new []{5,33}, 100)] + public void AllWithDifferentLengthBitSet(int[] values, int multiplier) { var bitSet1 = new BitSet(); - bitSet1.SetBit(5); + bitSet1.SetBit(values[0] * multiplier); var bitSet2 = new BitSet(); - bitSet2.SetBit(33); + bitSet2.SetBit(values[1] * multiplier); var allResult = bitSet2.All(bitSet1); var anyResult = bitSet2.Any(bitSet1); diff --git a/src/Arch/Core/Utils/BitSet.cs b/src/Arch/Core/Utils/BitSet.cs index 2b012ac3..0d623bdd 100644 --- a/src/Arch/Core/Utils/BitSet.cs +++ b/src/Arch/Core/Utils/BitSet.cs @@ -324,26 +324,56 @@ public bool None(BitSet other) [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exclusive(BitSet other) { - var bits = _bits.AsSpan(); - var otherBits = other._bits.AsSpan(); - var count = Math.Min(_bits.Length, otherBits.Length); + var max = (_highestBit/sizeof(uint)/Padding)+1; + var min = Math.Min(Math.Min(Length, other.Length), max); - for (var i = 0; i < count; i++) + if (!Vector.IsHardwareAccelerated || min < Padding) { - var bit = bits[i]; - if ((bit ^ otherBits[i]) != 0) + var bits = _bits.AsSpan(); + var otherBits = other._bits.AsSpan(); + + // Bitwise xor, if both are not totally equal, return false + for (var i = 0; i < min; i++) { - return false; + var bit = bits[i]; + if ((bit ^ otherBits[i]) != 0) + { + return false; + } } - } - // handle extra bits on our side that might just be all zero - var bitCount = _bits.Length; - for (var i = count; i < bitCount; i++) + // handle extra bits on our side that might just be all zero + for (var i = min; i < max; i++) + { + if (bits[i] != 0) + { + return false; + } + } + } + else { - if (bits[i] != 0) + // Vectorized bitwise xor, return true since any is met + for (var i = 0; i < min; i += Padding) + { + var vector = new Vector(_bits, i); + var otherVector = new Vector(other._bits, i); + + var resultVector = Vector.Xor(vector, otherVector); + if (!Vector.EqualsAll(resultVector, Vector.Zero)) + { + return false; + } + } + + // Handle extra bits on our side that might just be all zero. + for (var i = min; i < max; i += Padding) { - return false; + var vector = new Vector(_bits, i); + if (!Vector.EqualsAll(vector, Vector.Zero)) // Vectors are not zero bits[0] != 0 basically + { + return false; + } } }