优化x86的逻辑AND操作

问题描述 投票:2回答:2

我正在尝试优化掩盖数组的算法。初始代码如下所示:

void mask(unsigned int size_x, unsigned int size_y, uint32_t *source, uint32_t *m)
{
    unsigned int rep=size_x*size_y;
    while (rep--)
    {            
        *(source++) &= *(m++);
    }
}

我试图做Loop Unrolling + prefetching

void mask_LU4(unsigned int size_x, unsigned int size_y, uint32_t *source, uint32_t   *mask)
{                             // in place
    unsigned int rep;
    rep= size_x* size_y;
    rep/= 4 ; 
    while (rep--) 
    {              
        _mm_prefetch(&source[16], _MM_HINT_T0);
        _mm_prefetch(&mask[16], _MM_HINT_T0);
        source[0] &= mask[0];
        source[1] &= mask[1];
        source[2] &= mask[2];
        source[3] &= mask[3];
        source += 4;
        mask += 4;
    }
}

并使用内在函数

void fmask_SIMD(unsigned int size_x, unsigned int size_y, uint32_t *source, uint32_t *mask)
{                             // in place
    unsigned int rep;
    __m128i *s,*m ;
    s = (__m128i *) source;
    m = (__m128i *) mask;
    rep= size_x* size_y;
    rep/= 4 ; 
    while (rep--) 
    {               
        *s = _mm_and_si128(*s,*m);
        source+=4;mask+=4; 
        s = (__m128i *) source;
        m = (__m128i *) mask;
    }   
}  

但是性能是一样的。我试图对SIMD和循环展开版本执行sw预取,我看不到任何改进。关于如何优化此算法的任何想法?

P.S.1:我正在使用gcc 4.8.1,我用-march=native-Ofast编译。

P.S.2:我使用的是英特尔酷睿i5 3470 @ 3.2Ghz,Ivy桥架构。 L1 DCache 4X32KB(8路),L2 4x256,L3 6MB,RAM-DDR3 4Gb(双通道,DRAM @ 798,1Mhz)

gcc optimization x86 simd bitmask
2个回答
2
投票

您的操作是内存带宽限制。但是,这并不一定意味着您的操作正在实现最大内存带宽。要接近最大内存带宽,您需要使用多个线程。使用OpenMP(将-fopenmp添加到GCC的选项),您可以这样做:

#pragma omp parallel for
for(int i=0; i<rep; i++) { source[i] &= m[i]; }

如果您不想修改源但使用不同的目标,那么您可以使用如下的流指令:

#pragma omp parallel for
for(int i=0; i<rep/4; i++) {
    __m128i m4 = _mm_load_si128((__m128i*)&m[4*i]);
    __m128i s4 = _mm_load_si128((__m128i*)&source[4*i]);
    s4 = _mm_and_si128(s4,m4);
    _mm_stream_si128((__m128i*i)&dest[4*i], s4);
}

这不会比使用相同的目标和源更快。但是,如果您已经计划使用不等于源的目标,那么使用rep可能会更快(对于_mm_store_si128的某个值)。


-1
投票

您的问题可能是内存限制,但这并不意味着您无法在每个周期处理更多内容。通常当你有一个低负载操作(就像你在这里,它毕竟只是一个AND),组合许多负载和存储是有意义的。在大多数CPU中,大多数负载将由L2高速缓存组合成单个高速缓存线负载(特别是如果它们是连续的)。我建议在这里增加循环展开至少4个SIMD数据包,以及预取。你仍然受内存限制,但你会得到更少的缓存未命中,给你一个稍微好一点的性能。

© www.soinside.com 2019 - 2024. All rights reserved.