优化x86的逻辑AND操作

时间:2014-11-26 15:46:19

标签: gcc optimization x86 simd bitmask

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

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++);
    }
}

我尝试过循环展开+预取

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和Loop Unrolling版本执行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)

2 个答案:

答案 0 :(得分: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 :(得分:-1)

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