What is effective(fast) way to get last set bit in BitArray. (LINQ or simple backward for loop isn't very fast for large bitmaps. And I need fast) BitArray I see next algorithm: go back through BitArray internal int array data and use some compiler Intrinsic Like C++ _BitScanReverse( don't know analog in C#).
-
use `Linq` => `arrayInstance.Last()` – Sebastian Siemens May 06 '16 at 11:24
-
It would be easier in an integral type, so if your array is short enough you might consider that. – harold May 06 '16 at 11:25
-
Linq is not fast. I Sad - "effective". No LINQ! – Brans Ds May 06 '16 at 11:38
-
You can do that, you just have to implement the bitscan [yourself](http://stackoverflow.com/q/15967240/555045) – harold May 06 '16 at 11:46
-
I assume that you do more than *just* scanning the bitarray. Do you also *manipulate* the array? If so, is it possible to employ secondary data structures to assist you in this task? – Damien_The_Unbeliever May 06 '16 at 11:46
-
2The BitArray class only provides access to individual bits, so you can't efficiently skip whole words that are all-zero. You'll have to find/write another implementation of a bit array. – Daniel May 06 '16 at 11:48
-
@ Damien_The_Unbeliever It is possible.. it will be good to not copy data from BitArray internal array and just use it. – Brans Ds May 06 '16 at 11:48
3 Answers
The "normal" solution:
static long FindLastSetBit(BitArray array)
{
for (int i = array.Length - 1; i >= 0; i--)
{
if (array[i])
{
return i;
}
}
return -1;
}
The reflection solution (note - relies on implementation of BitArray
):
static long FindLastSetBitReflection(BitArray array)
{
int[] intArray = (int[])array.GetType().GetField("m_array", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(array);
for (var i = intArray.Length - 1; i >= 0; i--)
{
var b = intArray[i];
if (b != 0)
{
var pos = (i << 5) + 31;
for (int bit = 31; bit >= 0; bit--)
{
if ((b & (1 << bit)) != 0)
return pos;
pos--;
}
return pos;
}
}
return -1;
}
The reflection solution is 50-100x faster for me on large BitArray
s (on very small ones the overhead of reflection will start to appear). It takes about 0.2 ms per megabyte on my machine.
The main thing is that if (b != 0)
checks 32 bits at once. The inner loop which checks specific bits only runs once, when the correct word is found.
Edited: unsafe code removed because I realized almost nothing is gained by it, it only avoids the array boundary check and as the code is so fast already it doesn't matter that much. For the record, unsafe solution (~30% faster for me):
static unsafe long FindLastSetBitUnsafe(BitArray array)
{
int[] intArray = (int[])array.GetType().GetField("m_array", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(array);
fixed (int* buffer = intArray)
{
for (var i = intArray.Length - 1; i >= 0; i--)
{
var b = buffer[i];
if (b != 0)
{
var pos = (i << 5) + 31;
for (int bit = 31; bit >= 0; bit--)
{
if ((b & (1 << bit)) != 0)
return pos;
pos--;
}
return pos;
}
}
}
return -1;
}

- 5,495
- 5
- 25
- 43
If you want the index of that last set bit you can do this in C# 6.
int? index = array.Select((b,i)=>{Index = i, Value = b})
.LastOrDefault(x => x.Value)
?.Index;
Otherwise you have to do something like this
var last = array.Select((b,i)=>{Index = i, Value = b})
.LastOrDefault(x => x.Value);
int? index = last == null ? (int?)null : last.Index;
Either way the index
will be null
if all the bits are zero.

- 31,741
- 4
- 58
- 93
-
-
@BransDs "effective" is subjective. You should be more describtive if you want more specific answers. In any case you'll have to loop through the `BitArray` which is exactly what Linq does. Otherwise if you want to use bit operators for this then you need to use a more primitive type than `BitArray`. – juharr May 06 '16 at 11:46
I don't believe there is anything it can be done, other than iterate from last to first bit, and ask for each one if it is set. It could be done with something like:
BitArray bits = ...;
int lastSet = Enumerable.Range(1, bits.Length)
.Select(i => bits.Length - i)
.Where(i => bits[i])
.DefaultIfEmpty(-1)
.First();
That should return the last bit set, or -1 if none is. Haven't tested it myself, so it may need some adjustment.
Hope it helps.

- 3,928
- 1
- 20
- 28
-
-
You can replace the usage of LINQ with a simple `for` from the end of the BitArray to the front. – Fede May 06 '16 at 12:05
-
I see now that you edited the question to ask specifically for no linq and not iterate from end to front. I don't think BitArray is going to suit your needs then. I would go with a custom BitArray implementation that allow you access to the internal int/long array so that you can ask if 32/64 bits at a time are all 0. – Fede May 06 '16 at 12:12