c# - 最快的方法从部分位获取整数
Posted
技术标签:
【中文标题】c# - 最快的方法从部分位获取整数【英文标题】:c# - fastest method get integer from part of bits 【发布时间】:2020-12-26 12:43:24 【问题描述】:我有byte[] byteArray,通常是byteArray.Length = 1-3
我需要将数组分解为位,取一些位(例如,5-17),然后将这些位转换为 Int32。
我试过这样做
private static IEnumerable<bool> GetBitsStartingFromLSB(byte b)
for (int i = 0; i < 8; i++)
yield return (b % 2 != 0);
b = (byte)(b >> 1);
public static Int32 Bits2Int(ref byte[] source, int offset, int length)
List<bool> bools = source.SelectMany(GetBitsStartingFromLSB).ToList();
bools = bools.GetRange(offset, length);
bools.AddRange(Enumerable.Repeat(false, 32-length).ToList() );
int[] array = new int[1];
(new BitArray(bools.ToArray())).CopyTo(array, 0);
return array[0];
但是这个方法太慢了,不得不经常调用。
我怎样才能更有效地做到这一点?
非常感谢!现在我这样做:
public static byte[] GetPartOfByteArray( byte[] source, int offset, int length)
byte[] retBytes = new byte[length];
Buffer.BlockCopy(source, offset, retBytes, 0, length);
return retBytes;
public static Int32 Bits2Int(byte[] source, int offset, int length)
if (source.Length > 4)
source = GetPartOfByteArray(source, offset / 8, (source.Length - offset / 8 > 3 ? 4 : source.Length - offset / 8));
offset -= 8 * (offset / 8);
byte[] intBytes = new byte[4];
source.CopyTo(intBytes, 0);
Int32 full = BitConverter.ToInt32(intBytes);
Int32 mask = (1 << length) - 1;
return (full >> offset) & mask;
而且运行速度非常快!
【问题讨论】:
我认为这个问题更适合 codereview.stackexchange.com @FildorBitConverter
在这种情况下基本上是零使用;和 IMO 几乎任何其他;事实上,BitConverter
上唯一真正有用的方法是 .ToString()
,作为在单元测试等中编写 hex 的一种懒惰方式,也许 .IsLittleEndian
可以查询 CPU 字节序跨度>
@MarcGravell 不幸的是,我不得不同意。
【参考方案1】:
如果您追求“快速”,那么最终您需要使用位逻辑来执行此操作,而不是 LINQ 等。我不会编写实际代码,但您需要:
使用/ 8
和% 8
的偏移量来查找起始字节 和该字节内的位偏移量
无论您需要多少字节,都可以组合 - 如果您使用 32 位数字,则很可能最多 5 个(因为可能存在偏移)
;例如进入long
,无论您期望采用哪种字节序(可能是大字节序?)
对组合值使用右移 (>>
) 以删除应用位偏移所需的许多位(即value >>= offset % 8;
)
屏蔽掉你不想要的任何位;例如value &= ~(-1L << length);
(-1
给你全一;<< length
在右手边创建length
零,~
将所有零交换为一,将一交换为零,所以你现在有length
右边的)
如果值是有符号的,您需要考虑如何处理负数,尤其是在您不总是读取 32 位的情况下
【讨论】:
【参考方案2】:首先,您要求优化。但你所说的只有:
太慢了 需要经常调用它没有关于:
的信息 慢多少才算太慢?你测量过当前的代码吗?您估计过需要多快吗? “经常”的频率是多少? 源字节数组有多大? 等可以通过多种方式进行优化。当要求优化时,一切都很重要。例如,如果 source byte[] 是 1 或 2 个字节长(是的,可能很荒谬,但你没有告诉我们),如果它很少改变,那么你可以获得非常好的结果通过缓存结果。以此类推。
所以,我没有解决方案,只是列出了可能的性能问题:
private static IEnumerable<bool> GetBitsStartingFromLSB(byte b) // A
for (int i = 0; i < 8; i++)
yield return (b % 2 != 0); // A
b = (byte)(b >> 1);
public static Int32 Bits2Int(ref byte[] source, int offset, int length)
List<bool> bools = source.SelectMany(GetBitsStartingFromLSB).ToList(); //A,B
bools = bools.GetRange(offset, length); //B
bools.AddRange(Enumerable.Repeat(false, 32-length).ToList() ); //C
int[] array = new int[1]; //D
(new BitArray(bools.ToArray())).CopyTo(array, 0); //D
return array[0]; //D
答:LINQ 很有趣,但除非小心操作,否则不会很快。对于每个输入字节,它需要 1 个字节,将其拆分为 8 个布尔值,然后将它们传递给编译器生成的 IEnumerable 对象 *)。请注意,这一切也需要稍后清理。可能您只需返回new bool[8]
甚至BitArray(size=8)
即可获得更好的性能。
*) 从概念上讲。实际上yield-return是惰性的,所以它不是8valueobj+1refobj,而只是1个可枚举的生成项目。但是,你在 (B) 中执行 .ToList(),所以我以这种方式写这篇文章与事实相去甚远。
A2:8 是硬编码的。一旦你放弃了漂亮的 IEnumerable 并将其更改为类似常量大小的数组,你可以预先分配该数组并通过参数将其传递给 GetBitsStartingFromLSB 以进一步减少创建和随后丢弃的临时对象的数量。而且由于 SelectMany 一个接一个地访问项目而无需返回,因此可以重用该预分配的数组。
B:将整个 Source 数组转换为字节流,将其转换为 List。然后丢弃整个列表,除了该列表的一小部分偏移长度范围。为什么要隐蔽列出呢?这只是浪费了另一组对象,并且内部数据也被复制了,因为bool
是一个值类型。您可以通过 .Skip(X).Take(Y) 直接从 IEnumerable 获取范围
C:填充布尔列表以包含 32 个项目。 AddRange/Repeat 很有趣,但 Repeat 必须返回一个 IEnumerable。它又是另一个被创建并丢弃的对象。您正在使用false
填充列表。放弃列表的想法,使其成为 bool[32]。或位数组(32)。它们自动以false
开头。这是bool
的默认值。遍历“范围”A+B 中的那些位,并按索引将它们写入该数组。那些书面的将有它们的价值,那些不成文的将保持虚假。工作完成,没有浪费任何东西。
C2:将预分配的 32 项数组与 A+A2 连接。 GetBitsStartingFromLSB 不需要返回任何东西,它可能会通过参数获得一个要填充的缓冲区。并且该缓冲区不需要是 8 项缓冲区。您可以传递整个 32 项的最终数组,并传递一个偏移量,以便该函数准确地知道在哪里写入。浪费的物品更少。
D:最后,所有将选定位作为整数返回的工作。新的临时数组被创建和浪费,新的 BitArray 也被有效地创建和浪费。请注意,之前您已经在 GetBitsStartingFromLSB 中进行了手动位移转换 int->bits,为什么不创建一个类似的方法来进行一些位移并改为使用 bits->int?如果您知道位的顺序,那么您现在也知道它们了。不需要数组和位数组,不需要一些代码摆动,并且您可以再次节省分配和数据复制。
我不知道这将为您节省多少时间/空间/等,但这只是乍一看突出的几点,无需过多地修改您对代码的原始想法,无需全部完成一口气通过数学和位移等。我已经看到 MarcGravell 也已经给你写了一些提示。如果您有空闲时间,我建议您先一个一个地尝试,看看每个更改如何(如果有的话!)每个更改都会影响性能。只是去看看。然后,您可能会放弃这一切,并在 Marc 的提示下再次尝试新的“通过数学和位移一次性完成所有操作”版本。
【讨论】:
以上是关于c# - 最快的方法从部分位获取整数的主要内容,如果未能解决你的问题,请参考以下文章
在C中找到整数中最高设置位(msb)的最快/最有效方法是啥?