在C#中,BitArray获取比特值比使用逐位移位的简单结合更快吗?

1)。 var bitValue = (byteValue & (1 << bitNumber)) != 0;

2)。 使用带有Get(int index)方法的System.Collections.BitArray

  • 什么更快?
  • 在.NET项目的什么情况下, BitArray比按位移位的简单连接更有用?

BitArray将能够处理任意数量的布尔值,而一个byte只能容纳8, int只能容纳32,等等。这将是两者之间的最大差异。

此外, BitArray实现了IEnumerable ,其中整数类型显然没有。 所以这一切都取决于你的项目的要求; 如果你需要IEnumerable或类似数组的接口,那么请使用BitArray

实际上,我会在两种解决方案中使用bool[] ,因为它更明确地表示您要跟踪的数据类型。 Ť

BitArraybitfield将使用bool[]大约1/8空间,因为它们将8个布尔值“打包”到一个字节中,而bool本身将占用整个8位字节。 使用bitfield或BitArray的空间优势并不重要,直到你存储大量bools 。 (数学留给读者:-))


基准

结果:对于我的原始测试环境,看起来BitArray 有点快,但与使用整数类型自己做同样的数量级。 还测试了一个bool[] ,这是最快的毫不奇怪。 访问内存中的单个字节将比访问不同字节中的各个位复杂得多。

 Testing with 10000000 operations: A UInt32 bitfield took 808 ms. A BitArray (32) took 574 ms. A List(32) took 436 ms. 

码:

 class Program { static void Main(string[] args) { Random r = new Random(); r.Next(1000); const int N = 10000000; Console.WriteLine("Testing with {0} operations:", N); Console.WriteLine(" A UInt32 bitfield took {0} ms.", TestBitField(r, N)); Console.WriteLine(" A BitArray (32) took {0} ms.", TestBitArray(r, N)); Console.WriteLine(" A List(32) took {0} ms.", TestBoolArray(r, N)); Console.Read(); } static long TestBitField(Random r, int n) { UInt32 bitfield = 0; var sw = Stopwatch.StartNew(); for (int i = 0; i < n; i++) { SetBit(ref bitfield, r.Next(32), true); bool b = GetBit(bitfield, r.Next(32)); SetBit(ref bitfield, r.Next(32), b); } sw.Stop(); return sw.ElapsedMilliseconds; } static bool GetBit(UInt32 x, int bitnum) { if (bitnum < 0 || bitnum > 31) throw new ArgumentOutOfRangeException("Invalid bit number"); return (x & (1 << bitnum)) != 0; } static void SetBit(ref UInt32 x, int bitnum, bool val) { if (bitnum < 0 || bitnum > 31) throw new ArgumentOutOfRangeException("Invalid bit number"); if (val) x |= (UInt32)(1 << bitnum); else x &= ~(UInt32)(1 << bitnum); } static long TestBitArray(Random r, int n) { BitArray b = new BitArray(32, false); // 40 bytes var sw = Stopwatch.StartNew(); for (int i = 0; i < n; i++) { b.Set(r.Next(32), true); bool v = b.Get(r.Next(32)); b.Set(r.Next(32), v); } sw.Stop(); return sw.ElapsedMilliseconds; } static long TestBoolArray(Random r, int n) { bool[] ba = new bool[32]; var sw = Stopwatch.StartNew(); for (int i = 0; i < n; i++) { ba[r.Next(32)] = true; bool v = ba[r.Next(32)]; ba[r.Next(32)] = v; } sw.Stop(); return sw.ElapsedMilliseconds; } } 

@Jonathon Reinhart,

不幸的是,你的基准是不确定的。 它没有考虑可能的延迟加载,缓存和/或预取(由CPU,主机OS和/或.NET运行时)的影响。

随机测试的顺序(或多次调用测试方法),您可能会注意到不同的时间测量。

我使用“任何CPU”平台目标和.NET 4.0客户端配置文件构建了原始基准测试,在我的机器上运行i7-3770 CPU和64位Windows 7。

我得到的是这个:

 Testing with 10000000 operations: A UInt32 bitfield took 484 ms. A BitArray (32) took 459 ms. A List(32) took 393 ms. 

这几乎符合你的观察。

但是,在UInt32测试之前执行BitArray测试产生了这样的结果:

 Testing with 10000000 operations: A BitArray (32) took 513 ms. A UInt32 bitfield took 456 ms. A List(32) took 417 ms. 

通过查看UInt32和BitArray测试的时间,您会注意到测量的时间似乎与测试本身无关,而是与测试运行的顺序相关。

为了至少缓解这些副作用,我在每个程序运行中执行了两次测试方法,结果如下。

测试订单UInt32,BitArray,BoolArray,UInt32,BitArray,BoolArray

 Testing with 10000000 operations: A UInt32 bitfield took 476 ms. A BitArray (32) took 448 ms. A List(32) took 367 ms. A UInt32 bitfield took 419 ms. <<-- Watch this. A BitArray (32) took 444 ms. <<-- Watch this. A List(32) took 388 ms. 

测试订单BitArray,UInt32,BoolArray,BitArray,UInt32,BoolArray

 Testing with 10000000 operations: A BitArray (32) took 514 ms. A UInt32 bitfield took 413 ms. A List(32) took 379 ms. A BitArray (32) took 444 ms. <<-- Watch this. A UInt32 bitfield took 413 ms. <<-- Watch this. A List(32) took 381 ms. 

看一下测试方法的第二次调用,看来至少在具有最新.NET运行时的i7 CPU上, UInt32测试比BitArray测试更快 ,而BoolArray测试仍然是最快的。

(我很抱歉,我必须将我对Jonathon基准测试的回复作为答案,但作为新的SO用户,我不能发表评论……)

编辑:

在调用第一个测试之前,您可以尝试放置Thread.Sleep(5000)或类似的权限,而不是改变测试方法的顺序……

此外,原始测试似乎使UInt32测试处于不利地位,因为它包括边界检查“ if(bitnum <0 || bitnum> 31) ”,执行3000万次。 其他两个测试都没有包括这样的边界检查。 然而,这实际上并非全部,因为BitArray和bool数组都在内部进行边界检查。

虽然我没有测试,但我希望消除边界检查会使UInt32和BoolArray测试的表现相似,但这对公共API来说不是一个好主张。