检查字节数组是否只包含1个值的最快方法

时间:2016-05-27 09:41:38

标签: c# performance bytearray

Oke现在显然我可以检查字节数组是否只包含1个值,但我不知道这是否是最快的方法。问题是有时候我会得到一个只有FF(255)值的字节数组,如果发生这种情况我需要在接下来的代码中忽略它,所以我做的事情如下:

var onlyFF = true;
foreach(var value in programCode)
{
    if (value != 0xFF)
    {
        onlyFF = false;
        break;
    }
}

但这是最快的方式吗?我将不得不检查大量的阵列(尽管(350)所有阵列都非常小)

这是最快的方式还是有更好的方法来做到这一点?

3 个答案:

答案 0 :(得分:3)

当然,有更快的方法来优化您正在执行的特定检查。一些评论已经表明的真正问题是它是否真的有必要?是否值得优化查询真的取决于几个问题,你必须首先问自己。

  1. 您的表现标准是什么?

    您应该每秒处理多少个数组?如果 答案是1000或更少,那我绝对不会打扰 试图优化代码。如果答案是数百万个数组a 第二,那么你可能想考虑做一些表现 测试你的代码。

  2. 您期望什么类型的数据?

    如果您处理的99%的缓冲区有效(并非所有0xFF字节),那么在大多数情况下,您的循环很可能在前几次检查中存在。如果仅适用于工作量的1%,那么针对最坏情况优化算法是否有意义。

  3. 通过更改比较方法,我会为您的代码带来哪些风险?风险大于风险?

  4. Adwaenyth提到的常用优化技术可以应用于您的情况。您可以将字节数组视为long数组,然后使用XOR位逻辑运算符一次比较8个字节。为了有效地使用此方法而无需复制缓冲区,您必须使用不安全的代码。以下示例显示了如何完成此操作的快速而脏的实现(请注意,我没有测试过此代码,所以请不要在没有正确测试的情况下使用):

        public static bool IsBufferValidUnSafeXOR(byte[] buffer)
        {
            bool isValid = false;
    
            int byteLength = buffer.Length;
            int base64Length = byteLength >> 3;  // same as -- > (int)(byteLength / 8);
            int remainingBytes = byteLength - (base64Length << 3);
            ulong toggleMask = 0xFFFFFFFFFFFFFFFF;
    
            unsafe 
            {
                fixed (byte* pByteBuffer = buffer)
                {
                    ulong* pBuffer = (ulong*)pByteBuffer;
                    int index = 0;
    
                    while (index < base64Length)
                    {
                        if ((pBuffer[index] ^ toggleMask) > 0)
                        {
                            isValid = true;
                            break;
                        }
    
                        index++;
                    }
    
                }
            }
    
            // Check remainder of byte array
            if (!isValid)
            {
                int index = (base64Length << 3);
    
                while(index < byteLength)
                {
                    if (buffer[index] != 0xFF)
                    {
                        isValid = true;
                        break;
                    }
    
                    index++;
                }
    
            }
    
            return isValid;
        }
    

    我对您当前的非优化方法和优化方法进行了几次性能比较。我在循环中执行每个方法,检查150万个缓冲区的有效性。对于第一次测试,仅检查的5%的缓冲区无效。第二次检查33%的缓冲区对于第3个50%和第4个100%无效。 下表显示了两种方法的比较方式:

    ---------------------------------------------------------------------------------------------
    | Nr | Total Nr.        | Valid Buffer  | Invalid Buffer    | Std Method    | XOR Unsafe    |
    |    | Buffers Checked  | Count         | Count             | Execution Time| Execution Time|
    ---------------------------------------------------------------------------------------------
    | 1  | 1,500,00         | 1,425,000     | 75,000            | 183 ms        | 124 ms        |
    ---------------------------------------------------------------------------------------------
    | 2  | 1,500,00         | 1,000,000     | 500,000           | 566 ms        | 226 ms        |
    ---------------------------------------------------------------------------------------------
    | 3  | 1,500,00         | 750,000       | 750,000           | 800 ms        | 259 ms        |
    ---------------------------------------------------------------------------------------------
    | 4  | 1,500,00         | 0             | 1,500,000         | 1574 ms       | 431 ms        |
    ---------------------------------------------------------------------------------------------
    

    从上表中我们可以看到,虽然不安全(XOR)方法速度更快,但如果只检查了5%的缓冲区无效,速度差异是微不足道的,而如果100%的缓冲区获得最大的性能提升无效。这让我们回到最初的问题是优化代码真的值得吗?

答案 1 :(得分:1)

使速度更快的一种相当简单的方法是获取数组的ulong*并一次比较8个字节的块与0xFFFFFFFFFFFFFFFFUL。您可能需要通过比较字节来处理数组开始和结束时的错位。

然后你可以将循环展开4次,以减少循环开销几乎为零。很快(但可能)比这更快地完成它。

另一个相当简单的选择是在C和PInvoke中编写它。 C编译器有很复杂的方法来实现这一点。 .NET JIT没有。虽然我对neither GCC nor LLVM do any particular tricks here感到惊讶。

使用不同的代码模式LLVM提供以下优化:

if (array[i + 0] & array[i + 1] & array[i + 2] & array[i + 3] == 0xFF)
 return true;

这节省了很多指令和分支。

答案 2 :(得分:0)

对我来说,这听起来像是一个可并行化的问题。 如果你有数百个这样的数组,里面有几百个字节,我会考虑使用GPU

你可以使用CUDA&#34;只能使用Nvidia卡&#34;或OpenCL&#34;在所有卡上工作&#34;解决这个任务。

对于c#,有一个很好的lib(对于OpenCL),名为cloo,易于使用