翻转字节数组 - 提高性能

时间:2014-02-13 04:04:35

标签: c# arrays performance bitarray

我有一些代码管理从传感器阵列接收的数据。控制传感器的PIC使用8个SAR-ADC并行读取4096个数据字节。这意味着它读取前8个字节的最高有效位;然后它读取第二位,依此类推,直到第八位(最低位) 基本上,对于它读取的每8个字节,它创建(并向计算机发送)8个字节,如下所示:

// rxData[0] = MSB[7] MSB[6] MSB[5] MSB[4] MSB[3] MSB[2] MSB[1] MSB[0]
// rxData[1] = B6[7] B6[6] B6[5] B6[4] B6[3] B6[2] B6[1] B6[0]
// rxData[2] = B5[7] B5[6] B5[5] B5[4] B5[3] B5[2] B5[1] B5[0]
// rxData[3] = B4[7] B4[6] B4[5] B4[4] B4[3] B4[2] B4[1] B4[0]
// rxData[4] = B3[7] B3[6] B3[5] B3[4] B3[3] B3[2] B3[1] B3[0]
// rxData[5] = B2[7] B2[6] B2[5] B2[4] B2[3] B2[2] B2[1] B2[0]
// rxData[6] = B1[7] B1[6] B1[5] B1[4] B1[3] B1[2] B1[1] B1[0]
// rxData[7] = LSB[7] LSB[6] LSB[5] LSB[4] LSB[3] LSB[2] LSB[1] LSB[0]

对于系统读取和处理的所有4096个字节重复此模式。
想象一下,每个8字节读取是分开进行的,然后我们可以将它们视为8by8位数组。我需要在从左下角(LSB [7])到右上角(MSB [0])的对角线周围镜像这个数组。完成此操作后,生成的8by8位数组在其行中包含从传感器读取的正确数据字节。我以前在PIC控制器上执行此操作,使用左移等等,但这会大大减慢系统速度。因此,现在使用以下代码在我们处理数据的计算机上执行此操作:

BitArray ba = new BitArray(rxData);
BitArray ba2 = new BitArray(ba.Count);
for (int i = 0; i < ba.Count; i++)
{
    ba2[i] = ba[(((int)(i / 64)) + 1) * 64 - 1 - (i % 8) * 8 - (int)(i / 8) + ((int)(i / 64)) * 8];
}
byte[] data = new byte[rxData.Length];
ba2.CopyTo(data, 0);

请注意,此代码有效。
rxData是接收的字节数组 我用于循环中ba []索引的公式代码用于镜像上面描述的数组。

在别处检查数组的大小,以确保它始终包含正确的字节数(4096)。

到目前为止,这是我的问题的背景。

在我的系统的每个处理循环中,我需要执行两次镜像,因为我的数据处理是连续获取的两个数组之间的差异。速度对我的系统很重要(可能是处理的主要限制因素),镜像占我处理执行时间的10%到30%。

我想知道是否有其他解决方案可以与我的镜像代码进行比较,这可能会让我提高性能。使用BitArrays是我找到的唯一方法来处理接收字节中的不同位。

由于

3 个答案:

答案 0 :(得分:2)

您可能会发现BitVector比BitArray执行得更好。

  

对于布尔值和内部使用的小整数,BitVector32比BitArray更有效。 BitArray可以根据需要无限增长,但它具有类实例所需的内存和性能开销。相反,BitVector32仅使用32位。

http://msdn.microsoft.com/en-us/library/system.collections.specialized.bitvector32.aspx

如果你初始化一个BitVector32数组并对它们进行操作,它应该比你现在操作BitArray更快。

如果使用一个线程执行镜像,第二个线程执行连续读取分析,也可能会提高性能。 Task Parallel Library Dataflow为这种类型的解决方案提供了一个很好的框架。您可以使用一个源块来获取数据缓冲区,使用一个转换块来执行镜像,使用一个目标块来执行数据处理。

答案 1 :(得分:2)

显而易见的解决方案是提取位并再次组合它们。你可以用一个循环来做,但因为它同时使用左移和右移,否则你需要一个负移位量,所以我展开它以便更容易理解和更快速

out[0] = ((rxData[0] & 0x80)     )  | ((rxData[1] & 0x80) >> 1) | ((rxData[2] & 0x80) >> 2) | ((rxData[3] & 0x80) >> 3) |
         ((rxData[4] & 0x80) >> 4)  | ((rxData[5] & 0x80) >> 5) | ((rxData[6] & 0x80) >> 6) | ((rxData[7] & 0x80) >> 7);

out[1] = ((rxData[0] & 0x40) << 1)  | ((rxData[1] & 0x40)     ) | ((rxData[2] & 0x40) >> 1) | ((rxData[3] & 0x40) >> 2) |
         ((rxData[4] & 0x40) >> 3)  | ((rxData[5] & 0x40) >> 4) | ((rxData[6] & 0x40) >> 5) | ((rxData[7] & 0x40) >> 6);

out[2] = ((rxData[0] & 0x20) << 2)  | ((rxData[1] & 0x20) << 1) | ((rxData[2] & 0x20)     ) | ((rxData[3] & 0x20) >> 1) |
         ((rxData[4] & 0x20) >> 2)  | ((rxData[5] & 0x20) >> 3) | ((rxData[6] & 0x20) >> 4) | ((rxData[7] & 0x20) >> 5);

out[3] = ((rxData[0] & 0x10) << 3)  | ((rxData[1] & 0x10) << 2) | ((rxData[2] & 0x10) << 1) | ((rxData[3] & 0x10)     ) |
         ((rxData[4] & 0x10) >> 1)  | ((rxData[5] & 0x10) >> 2) | ((rxData[6] & 0x10) >> 3) | ((rxData[7] & 0x10) >> 4);

out[4] = ((rxData[0] & 0x08) << 4)  | ((rxData[1] & 0x08) << 3) | ((rxData[2] & 0x08) << 2) | ((rxData[3] & 0x08) << 1) |
         ((rxData[4] & 0x08)    )   | ((rxData[5] & 0x08) >> 1) | ((rxData[6] & 0x08) >> 2) | ((rxData[7] & 0x08) >> 3);

out[5] = ((rxData[0] & 0x04) << 5)  | ((rxData[1] & 0x04) << 4) | ((rxData[2] & 0x04) << 3) | ((rxData[3] & 0x04) << 2) |
         ((rxData[4] & 0x04) << 1)  | ((rxData[5] & 0x04)     ) | ((rxData[6] & 0x04) >> 1) | ((rxData[7] & 0x04) >> 2);

out[6] = ((rxData[0] & 0x02) << 6)  | ((rxData[1] & 0x02) << 5) | ((rxData[2] & 0x02) << 4) | ((rxData[3] & 0x02) << 3) |
         ((rxData[4] & 0x02) << 2)  | ((rxData[5] & 0x02) << 1) | ((rxData[6] & 0x02)     ) | ((rxData[7] & 0x02) >> 1);

out[7] = ((rxData[0] & 0x01) << 7)  | ((rxData[1] & 0x01) << 6) | ((rxData[2] & 0x01) << 5) | ((rxData[3] & 0x01) << 4) |
         ((rxData[4] & 0x01) << 3)  | ((rxData[5] & 0x01) << 2) | ((rxData[6] & 0x01) << 1) | ((rxData[7] & 0x01)     );

答案 2 :(得分:0)

实际上与get column in a bitboard problem相同,因此可以通过将字节数组视为64位整数来更有效地解决

byte get_byte(ulong matrix, uint col)
{
    const ulong column_mask = 0x8080808080808080ull;
    const ulong magic       = 0x2040810204081ull;

    ulong column = ((matrix << col) & column_mask) * magic;
    return (byte)(column >> 56);
}

// Actually the below step is not needed. You can read rxData directly into the `ulong`
// variable instead of a bit array. Remember to CHANGE THE ENDIANNESS if necessary
ulong matrix = (rxData[7] << 56) | (rxData[6] << 48) | (rxData[5] << 40) | (rxData[4] << 32)
             | (rxData[3] << 24) | (rxData[2] << 16) | (rxData[1] <<  8) | rxData[0];

for (int i = 0; i < 8; i++)
    data[i] = get_byte(matrix, i);

在较新的x86 CPU中,可以使用PDEP指令集中的BMI2指令。我不确定C#中是否有任何对应的内在函数。如果内部函数不存在,则必须使用这样的本机代码

data[i] = _pext_u64(matrix, column_mask << col);