C++ 使用Intel Intrinsic快速查找整数数组和

C++ 使用Intel Intrinsic快速查找整数数组和,c++,intrinsics,avx,avx2,C++,Intrinsics,Avx,Avx2,我一直在做一个在线判断的任务:实现int-sum(const-int*array,unsigned-int-len),这样它就会返回和的数组len可以是200000,这个函数可以调用200000次;我的程序必须在0.9秒内执行 目前,我的代码如下所示: #include <immintrin.h> #include <stdio.h> int sum(const int* array, unsigned int len) { register int i = 8

我一直在做一个在线判断的任务:实现
int-sum(const-int*array,unsigned-int-len)
,这样它就会返回和的数组
len
可以是200000,这个函数可以调用200000次;我的程序必须在0.9秒内执行

目前,我的代码如下所示:

#include <immintrin.h>
#include <stdio.h>

int sum(const int* array, unsigned int len) {
    register int i = 8, s = 0;
    __m256i sm = _mm256_loadu_si256((void *)(array));
    for (; i+8 < len; i += 8) {
        const __m256i x = _mm256_loadu_si256((void *)(array+i));
        sm = _mm256_add_epi32(sm, x);
    }
    sm = _mm256_hadd_epi32(sm, sm);
    sm = _mm256_hadd_epi32(sm, sm);
    s = _mm256_extract_epi32(sm, 0);
    s += _mm256_extract_epi32(sm, 4);
    for(; i < len; ++i) s += array[i];
    return s;
}
#包括
#包括
整数和(常量整数*数组,无符号整数len){
寄存器int i=8,s=0;
__m256i sm=_mm256_loadu_si256((空*)(数组));
对于(;i+8
但是,当法官报告超过了
时间限制时,该代码不会通过


有人能指出哪些指令在时间上是昂贵的,以及如何加速我的代码吗?

快速检查一下,看起来最新的处理器提供了两个加载端口和两个加法端口,因此至少从理论上讲,通过展开循环的两次迭代,您应该可以获得可观的收益(虽然如果数据非常大,它可能会很快下降到主内存的带宽)


与任何AVX操作一样,您希望确保正在使用的数据正确对齐。如果数据未对齐,较旧的处理器将出现故障。较新的处理器将正常工作,但您将受到相当严重的速度损失。

实施@JerryCoffin的建议:


#include <immintrin.h>
#include <stdio.h>

int sum(const int* array, unsigned int len) {
    if(len < 60) {
        int s = 0;
        for(int i = 0; i < len; ++i) s += array[i];
        return s;
    }
    register int i = 0, s = 0;
    __m256i sm = _mm256_loadu_si256((void *)(array+i));
    __m256i sm2 = _mm256_loadu_si256((void *)(array+i+8));
    i += 16;
    for (; i+16 < len; i += 16) {
        const __m256i x = _mm256_loadu_si256((void *)(array+i));
        sm = _mm256_add_epi32(sm, x);
        const __m256i y = _mm256_loadu_si256((void *)(array+i+8));
        sm2 = _mm256_add_epi32(sm2, y);
    }
    sm = _mm256_add_epi32(sm, sm2);
    sm = _mm256_hadd_epi32(sm, sm);
    sm = _mm256_hadd_epi32(sm, sm);
    s += _mm256_extract_epi32(sm, 0);
    s += _mm256_extract_epi32(sm, 4);
    for(; i < len; ++i) s += array[i];
    return s;
}


#包括
#包括
整数和(常量整数*数组,无符号整数len){
if(len<60){
int s=0;
对于(int i=0;i

有趣的是,由于函数被调用的次数太多,在数组对齐之前消耗整数实际上比使用
loadu

要花费更多的时间,因为函数被调用的次数太多-你的意思是说计数太少,所以启动开销会很高?是的,尤其是如果数据经常正确对齐,则将其保留为HW通常是好的。(虽然对于将相同字节重新处理两次是便宜的,例如复制并添加到非重叠输出中,未对齐且可能重叠的第一个向量可能是好的)。当L1d缓存中的数据不热时,支持AVX2的CPU通常会以足够快的速度吸收未对齐成本,以跟上L3或RAM(只有2%的慢),甚至大部分是L2。如果小的阵列性能很重要,使用一个更好的水平和(),并且考虑用一个不对齐的矢量负载来处理尾部,以避免双重计数。(如果奇数大小正常,也可以使用0..3
\uuu m128i
向量,将所需的标量清理量从0..15减少到0..3标量)但是,清理代码的分支预测可能会有问题;如果您可以分析在线法官的工作负载,请单击IDK。如果您的带宽有限,您可能希望签出评论,而不是用于扩展讨论;此对话已结束。