c#-从部分位获取整数的最快方法

c#-从部分位获取整数的最快方法,c#,arrays,performance,bit,C#,Arrays,Performance,Bit,我有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[]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];           
}
私有静态IEnumerable GetBitsStartingFromLSB(字节b)
{
对于(int i=0;i<8;i++)
{
收益率回报率(b%2!=0);
b=(字节)(b>>1);
}
}
公共静态Int32位2int(参考字节[]源,int偏移量,int长度)
{
List bools=source.SelectMany(GetBitsStartingFromLSB.ToList();
bools=bools.GetRange(偏移量、长度);
bools.AddRange(可枚举.Repeat(false,32长度).ToList());
int[]数组=新的int[1];
(新的位数组(bools.ToArray()).CopyTo(数组,0);
返回数组[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;
    }
公共静态字节[]GetPartOfByteArray(字节[]源,int偏移量,int长度)
{
字节[]retBytes=新字节[长度];
Buffer.BlockCopy(源、偏移量、retBytes、0、长度);
返回retBytes;
}
公共静态Int32位2int(字节[]源,int偏移量,int长度)
{
如果(source.Length>4)
{
source=GetPartOfByteArray(source,offset/8,(source.Length-offset/8>3?4:source.Length-offset/8));
偏移量-=8*(偏移量/8);
}
字节[]intBytes=新字节[4];
source.CopyTo(整数字节,0);
Int32 full=位转换器.ToInt32(intBytes);
Int32掩码=(1>偏移量)&掩码;
}
而且它工作得非常快

如果您想要“快速”,那么最终需要使用位逻辑,而不是LINQ等。我不打算编写实际代码,但您需要:

  • 使用偏移量与
    /8
    %8
    一起查找起始字节和该字节内的位偏移量
  • 无论需要多少字节,都可以组合—如果在32位数字之后,很可能最多5个字节(因为可能存在偏移) ; 例如,转换成一个
    长的
    ,以您期望的任何一个尾端(大概是大尾端?)为准
  • 在合成值上使用右移(
    >
    )可以删除应用位偏移量所需的位数(即
    值>>=偏移量%8;

  • 掩盖你不想要的任何东西;例如,
    value&=~-1L首先,你要求优化。但你所说的只有:

    • 太慢
    • 需要经常打电话吗
    没有关于以下方面的信息:

    • 有多慢就是太慢?你测量过当前的代码吗?你估计过你需要多快吗
    • “经常”是多久一次
    • 源byt阵列有多大
    • 等等
    优化可以通过多种方式进行。当要求优化时,一切都很重要。例如,如果源字节[]的长度为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
    }
    
    私有静态IEnumerable GetBitsStartingFromLSB(字节b)//A
    {
    对于(int i=0;i<8;i++)
    {
    收益率回报率(b%2!=0);//A
    b=(字节)(b>>1);
    }
    }
    公共静态Int32位2int(参考字节[]源,int偏移量,int长度)
    {
    List bools=source.SelectMany(GetBitsStartingFromLSB.ToList();//A,B
    bools=bools.GetRange(偏移量,长度);//B
    bools.AddRange(Enumerable.Repeat(false,32长度).ToList());//C
    int[]数组=新的int[1];//D
    (新的位数组(bools.ToArray()).CopyTo(数组,0);//D
    返回数组[0];//D
    }
    
    答:LINQ很有趣,但除非小心操作,否则不会很快。对于每个输入字节,它需要1个字节,将其拆分为8个布尔值,并将其传递给编译器生成的IEnumerable对象*)。请注意,所有这些也需要稍后清理。只需返回
    新bool[8]
    或甚至
    位数组(size=8)
    ,您可能会获得更好的性能

    *)在概念上。事实上,yield-return是惰性的,所以生成项的不是8valueobj+1refobj,而是1个可枚举项。但是,你在(B)中做了。ToList(),所以我用这种方式写这篇文章与事实并不遥远

    A2:8是硬编码的。一旦您删除了这个漂亮的IEnumerable并将其更改为一个大小恒定的数组,您就可以预先分配该数组,并通过参数将其传递给GetBitsStartingFromLSB,以进一步减少创建的临时对象的数量,以及后来丢弃的临时对象的数量。由于SelectMany一个接一个地访问项目而不必返回,因此预分配的数组可以重用

    B:将整个源数组转换为字节流,将其转换为列表。然后丢弃整个列表,除了该列表的小偏移长度范围。为什么要隐姓埋名?这只是又浪费了一包对象,内部数据也被复制了,因为
    bool
    是一个valuetype。您可以直接从IEnumerable by.Skip(X).Take(Y)中获取范围

    C:将布尔列表填充为32项。AddRange/Repeat很有趣,但Repeat必须返回IEnumerable。它又是另一个被创建并丢弃的对象。您正在使用
    false
    填充列表。放弃这个想法,把它变成一个傻瓜[32]。或位数组(32)。它们自动以
    false
    开头。这是
    bool
    的默认值。迭代“范围”A+B中的这些位,并按索引将它们写入该数组。写的有价值,不写的有虚假。工作完成,没有浪费任何物品

    C2:将预分配的32项数组与A+A2连接。GetBitsStartingFromLSB不需要返回任何内容,它可以通过参数获得要填充的缓冲区。缓冲区不需要是8项bu