C++ 查看固定长度数组之间的字节数相等的最快方法

C++ 查看固定长度数组之间的字节数相等的最快方法,c++,arrays,optimization,C++,Arrays,Optimization,我有两个包含16个元素(字符)的数组,我需要“比较”它们,看看这两个数组中有多少元素相等 这个例程将被使用数百万次(通常的运行次数约为6000万或7000万次),所以我需要它尽可能快。我正在C++(C++ Builder 2007,用于记录)< /P>工作 现在,我有一个简单的问题: matches += array1[0] == array2[0]; 重复16次(因为分析速度似乎比使用for循环快30%) 有没有其他方法可以更快地工作 有关环境和数据本身的一些数据: 我使用的是C++Bui

我有两个包含16个元素(字符)的数组,我需要“比较”它们,看看这两个数组中有多少元素相等

这个例程将被使用数百万次(通常的运行次数约为6000万或7000万次),所以我需要它尽可能快。我正在C++(C++ Builder 2007,用于记录)< /P>工作 现在,我有一个简单的问题:

matches += array1[0] == array2[0];
重复16次(因为分析速度似乎比使用for循环快30%)

有没有其他方法可以更快地工作

有关环境和数据本身的一些数据:

  • 我使用的是C++Builder,它没有任何速度优化需要考虑。我最终会尝试使用另一个编译器,但现在我只能使用这个
  • 大多数情况下,数据都会有所不同。100%相等的数据通常非常罕见(可能小于1%)

一句话是否更快

matches += (array1[0] == array2[0]) + (array1[1] == array2[1]) + ...;

如果您能够控制阵列的位置,例如在内存中一个接一个地放置阵列,则可能会在第一次访问时将其加载到CPU的缓存中

它取决于CPU及其缓存结构,并且在不同的机器上会有所不同


您可以在

中阅读有关内存层次结构和缓存的内容,如果写入速度比简单循环快16倍,那么您的编译器要么很差劲,要么没有启用优化


简短回答:没有更快的方法,除非你在并行硬件上做向量运算。

神奇的编译器选项会随着时间的推移而变化很大。特别是使其生成SSE矢量化可能会给您带来巨大的加速。

如果您需要绝对最低的占用空间,我会使用汇编代码。我已经有一段时间没有这样做了,但我敢打赌MMX(或者更可能是SSE2/3)有一些指令可以让您在很少的指令中完全做到这一点。

如果匹配是常见的情况,那么尝试将值加载为32位整数而不是16位整数,这样您就可以一次性比较2个(并将其计为2个匹配)

如果两个32位值相同,则必须分别测试它们(并找出顶部和底部的16位值)

代码将更加复杂,但应该更快


如果您的目标是64位系统,您可以对64位整数执行相同的操作,如果您真的想达到极限,请查看汇编器并使用各种基于向量的指令,这些指令可以让您一次处理128位。

尝试使用指针而不是数组:

p1 = &array1[0];
p2 = &array2[0];
match += (*p1++ == *p2++);
// copy 15 times.
当然,您必须将其与其他方法进行比较,以确定哪种方法最快


您确定此例程是您处理过程中的瓶颈吗?您是否真的通过优化它来提高整个应用程序的性能?同样,只有测量才能说明问题。

关键是使用CPU支持的最大寄存器进行比较,然后在必要时回退到字节

下面的代码演示了如何使用4字节整数,但如果在SIMD体系结构(任何现代Intel或AMD芯片)上运行,则可以在一条指令中比较两个阵列,然后返回到基于整数的循环。现在大多数编译器都具有对128位类型的内在支持,因此不需要ASM

(请注意,对于SIMD比较,您的阵列必须是16字节对齐的,而某些处理器(例如MIPS)将要求阵列是4字节对齐的,以便进行基于int的比较

例如


有什么方法可以修改数组的存储方式吗?考虑到您可能使用的是32位编译器,一次比较1个字节的速度非常慢。相反,如果您将16个字节存储在4个整数(32位)或2个长(64位)中,则只需分别执行4个或2个比较


要问自己的问题是,将数据存储为4个整数或2个长数组的成本是多少。您需要多久访问一次数据,等等。

总是有好的旧x86 REPNE CMPS指令。

更新:此答案已被修改,以使我的注释与下面提供的源代码相匹配

如果您能够使用SSE2和popcnt指令,则可以进行优化

16字节恰好适合于SSE寄存器。使用C++和汇编/内联函数,将两个16字节数组加载到XMM寄存器中,并将它们登记到CMP中。这样就生成了一个表示比较的真/假条件的位掩码。然后使用MOVMSK指令将位掩码的位表示加载到x86登记器中,然后成为这是一个位字段,您可以在其中对所有1进行计数,以确定有多少个真值。硬件popcnt指令可以快速对寄存器中的所有1进行计数

这需要了解汇编/内部函数,尤其是SSE。您应该能够找到这两者的web资源

如果在不支持SSE2或popcnt的计算机上运行此代码,则必须迭代数组并使用展开循环方法计算差异

祝你好运

编辑: 由于您表示不了解汇编,以下是一些示例代码来说明我的答案:

#include "stdafx.h"
#include <iostream>
#include "intrin.h"

inline unsigned cmpArray16( char (&arr1)[16], char (&arr2)[16] )
{
    __m128i first = _mm_loadu_si128( reinterpret_cast<__m128i*>( &arr1 ) );
    __m128i second = _mm_loadu_si128( reinterpret_cast<__m128i*>( &arr2 ) );

    return _mm_movemask_epi8( _mm_cmpeq_epi8( first, second ) );
}

int _tmain( int argc, _TCHAR* argv[] )
{
    unsigned count = 0;
    char    arr1[16] = { 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 };
    char    arr2[16] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 };

    count = __popcnt( cmpArray16( arr1, arr2 ) );

    std::cout << "The number of equivalent bytes = " << count << std::endl;

    return 0;
}
#包括“stdafx.h”
#包括
#包括“intrin.h”
内联无符号cmpArray16(字符(&arr1)[16],字符(&arr2)[16])
{
__m128i第一个=_mm_loadu_si128(重新解释铸件(&arr1));
__m128i秒=毫米负载si128(重新解释铸件(&arr2));
返回_mm_movemask_epi8(_mm_cmpeq_epi8(第一、第二));
}
int _tmain(int argc,_TCHAR*argv[]
{
无符号计数=0;
chararr1[16]={0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0};
chararr2[16]={1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0};
计数=uu popcnt(cmpArray16(arr1,arr2));

std::cout这必须是独立于平台的,还是这段代码总是在同一类型的CPU上运行
// depending on compiler you may have to insert the words via an intrinsic
__m128 qw1 = *(__m128*)byteArray[0];
__m128 qw2 = *(__m128*)byteArray[1];

// again, depending on the compiler the comparision may have to be done via an intrinsic
if (qw1 == qw2)
{
    same = 16;
}
else
{
    // do int/byte testing
}
#include "stdafx.h"
#include <iostream>
#include "intrin.h"

inline unsigned cmpArray16( char (&arr1)[16], char (&arr2)[16] )
{
    __m128i first = _mm_loadu_si128( reinterpret_cast<__m128i*>( &arr1 ) );
    __m128i second = _mm_loadu_si128( reinterpret_cast<__m128i*>( &arr2 ) );

    return _mm_movemask_epi8( _mm_cmpeq_epi8( first, second ) );
}

int _tmain( int argc, _TCHAR* argv[] )
{
    unsigned count = 0;
    char    arr1[16] = { 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 };
    char    arr2[16] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 };

    count = __popcnt( cmpArray16( arr1, arr2 ) );

    std::cout << "The number of equivalent bytes = " << count << std::endl;

    return 0;
}