2

I often need to compare two bit arrays to see if they are identical or not, so I came with a function that doesn't look too performant to me, seems like it could be better since there are three logical operations in a single line. I'm wonder if it's posible to simplify it.

Public Overloads Shared Function AreEqual(ByVal ba1 As BitArray, ByVal ba2 As BitArray) As Boolean

    If ba1.Length <> ba2.Length Then
        Return False
    End If

    Dim i As Integer
    For i = 0 To ba1.Length - 1
        If Not (Not ba1(i)) Xor ba2(i) Then
            Return False
        End If
    Next
    Return True

End Function

Edit:

After hours of testing and brainstorming, I came to my initial thoughts to reduce somehow the number of those three logical operators, and I put one of them into a variable, which means a single action to revert all bits at once instead of reverting bit by bit. This is the result, and is way much faster than previous one, about 3 times faster:

Public Overloads Shared Function AreEqual(ByVal ba1 As BitArray, ByVal ba2 As BitArray) As Boolean

    If ba1.Count <> ba2.Count Then
        Return False
    End If

    Dim revBa1 As BitArray = ba1.Not ' <<
    Dim i As Integer

    For i = 0 To ba1.Count - 1
        If (Not revBa1(i)) Xor ba2(i) Then ' eliminates one logical operation existing here before
            Return False
        End If
    Next
    Return True

End Function

Now, faster than this one? :) Who knows, maybe...

Edit 2:

After some more testing and unconclusive results I noticed that the second version of the function is incorrect. Although it gets the right result, but it changes the ba1 value like it were passed byRef to the function:

'------------------------
'ba1 before = 000000000000000000000000000000001
'ba2 before = 000000000000000000000000000000001
'ba1 after = 111111111111111111111111111111110
'ba2 after = 000000000000000000000000000000001
'function result = True
'------------------------

Any ideas?

Edit 3: The solution was simple, but the function isn't fast anymore:

Dim revBa1 As New BitArray(ba1)
    revBa1 = revBa1.Not()

Edit 4: Yes, this is it:

Public Overloads Shared Function AreEqual(ByVal ba1 As BitArray, ByVal ba2 As BitArray) As Boolean

    If ba1 Is Nothing OrElse ba2 Is Nothing Then
        Return False
    End If

    If ba1.Count <> ba2.Count Then
        Return False
    End If

    Dim i As Integer
    For i = 0 To ba1.Count - 1
        If ba1(i) Xor ba2(i) Then
            Return False
        End If
    Next
    Return True

End Function

I guess sometimes we don't see the forest because of trees :)

Sorin GFS
  • 477
  • 3
  • 18
  • 1
    `If ba1(i) <> ba2(i) Then`? – GSerg Sep 17 '17 at 20:01
  • I tested it, is slower, as I replayed to @Slai – Sorin GFS Sep 17 '17 at 21:27
  • How many elements in your `BitArray`? Would `Byte()` be a better choice? Or maybe even a `MemoryStream`? – SSS Sep 18 '17 at 02:53
  • The number of bits is big, up to 4kb. And I need to to be able to compare randomly bit by bit. I tested `Byte()` and isn't faster, I dont know much about `MemoryStream` and my data is already in memory. – Sorin GFS Sep 18 '17 at 10:20

1 Answers1

1

ba1(i) and ba2(i) return Booleans that can be compared directly:

Function AreEqual(ba1 As BitArray, ba2 As BitArray) As Boolean

    If ba1.Count <> ba2.Count Then Return False

    For i = 0 To ba1.Count - 1
        If ba1(i) <> ba2(i) Then Return False
    Next

    Return True
End Function

BitArray uses Integer array to store the bits, so for bigger BitArray (maybe about 100 bits? you might have to test that) it might be a bit faster to copy them to integer array before comparing:

Function AreEqual(ba1 As BitArray, ba2 As BitArray) As Boolean

    If ba1.Count <> ba2.Count Then Return False

    If ba1.Count < 64 Then                    ' needs some testing to determine this number
        For i = 0 To ba1.Count - 1
            If ba1(i) <> ba2(i) Then Return False
        Next
    Else
        Dim a1 = New Integer((ba1.Length - 1) \ 32) {}
        Dim a2 = New Integer((ba2.Length - 1) \ 32) {}

        ba1.CopyTo(a1, 0)
        ba2.CopyTo(a2, 0)

        For i = 0 To a1.Length - 1
            If a1(i) <> a2(i) Then Return False
        Next
    End If

    Return True
End Function

As a side note, shorter and slower way to compare enumerable collections is SequenceEquals:

Function AreEqual(ba1 As BitArray, ba2 As BitArray) As Boolean
    Return ba1.Cast(Of Boolean).SequenceEqual(ba2.Cast(Of Boolean))
End Function
Slai
  • 22,144
  • 5
  • 45
  • 53
  • thank you for your answer, Well, the first it seemed like to be the best. But I tested and it is not. Is about 30% slower, I dont know why. And, ofcourse I use it for bit arrays bigger than 100 bits. In addition, as I noticed the `bitarray.CopyTo(...` is slow. BitArray of integers is slow too. – Sorin GFS Sep 17 '17 at 21:24
  • 1
    30% slower than `If Not (Not ba1(i)) Xor ba2(i)` ? That seems a bit unusual .. are you testing with `Stopwatch`, optimizations enabled, and in Release mode? The fastest is actually to not use `BitArray`, but implement custom one (or copy the source code from referencesource.microsoft.com) so that you can compare the integer array directly without having to copy it, but that's too much effort for very small gain – Slai Sep 17 '17 at 21:35
  • yes, that would be too much for small gain. And I tested with stopwatch, optimizations enabled and disabled, very little difference, but not in release mode. Anyway, while both were tested in the same conditions... over and over again, my version gets between 550ms - 650 ms per milion and your version gets between 800ms - 900ms – Sorin GFS Sep 17 '17 at 21:49