2017-10-17 147 views
1

的我有一个函数:矢量模乘法

void Func(const int * a, const int * b, size_t size, int p, int * c) 
{ 
    for (size_t i = 0; i < size; ++i) 
     c[i] = (a[i]*b[i])%p; 
} 

执行该功能为整数的数组许多模乘法。 所有整数都是正数。 而且我需要改善它的表现。

我想到了SSE和AVX。但他们没有将模乘法向量化的操作。 或者我错了?

也许任何人都知道任何可能性来解决这个问题?

+0

这是一个素数。 – Michael

+2

你是否反复使用同一个'p'? “尺寸”通常有多大?如果它足够大或者重复的次数足够多,使用相同的'p',它可能甚至值得JIT编译一个使用硬编码向量移位的循环以及[定点乘法inverse](https://stackoverflow.com/问题/ 41183935 /为什么 - 不 - GCC使用乘法按一个怪号码功能于执行整数-迪维)。或者使用http://libdivide.com/来使用没有JIT的乘法反转,但是会有更多的开销(使用imm8计数的'psrlq'是最好的)。但它可能只有SSE2版本,而不是AVX2或AVX512。 –

+1

'a [i] * b [i]'是否曾经溢出?如果是的话,那会好吗,还是你想要64位结果mod'p'的结果? – chtz

回答

7

起初我想指出模操作可以使用浮点数字来实现:

d % p = d - int(float(d)/float(p))*p. 

虽然操作的右侧部分的量大然后在左边这部分是可取的,因为它可以使用SSE/AVX进行矢量化。

使用SSE4.1的实现:

void Func(const int * a, const int * b, size_t size, int p, int * c) 
{ 
    __m128 _k = _mm_set1_ps(1.0f/p); 
    __m128i _p = _mm_set1_epi32(p); 
    for (size_t i = 0; i < size; i += 4) 
    { 
     __m128i _a = _mm_loadu_si128((__m128i*)(a + i)); 
     __m128i _b = _mm_loadu_si128((__m128i*)(b + i)); 
     __m128i _d = _mm_mul_epi32(_a, _b); 
     __m128i _e = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(_d), _k)); // e = int(float(d)/float(p)); 
     __m128i _c = _mm_sub_epi32(_d, _mm_mul_epi32(_e, _p)); 
     _mm_storeu_si128((__m128i*)(c + i), _c); 
    }    
} 

使用AVX的实现:

void Func(const int * a, const int * b, size_t size, int p, int * c) 
{ 
    __m256 _k = _mm256_set1_ps(1.0f/p); 
    __m256i _p = _mm256_set1_epi32(p); 
    for (size_t i = 0; i < size; i += 8) 
    { 
     __m256i _a = _mm256_loadu_si128((__m256i*)(a + i)); 
     __m256i _b = _mm256_loadu_si128((__m256i*)(b + i)); 
     __m256i _d = _mm256_mul_epi32(_a, _b); 
     __m256i _e = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_cvtepi32_ps(_d), _k)); // e = int(float(d)/float(p)); 
     __m256i _c = _mm256_sub_epi32(_d, _mm256_mul_epi32(_e, _p)); 
     _mm256_storeu_si128((__m256i*)(c + i), _c); 
    }    
} 
+0

在pmulld为2个uops的CPU上(例如HSW和SKL),可能值得将整数输入转换为浮点值,然后再乘以。但'cvtdq2ps'运行在add单元上,所以它本身的吞吐量有限。事实上,它可能在HSW/SKL上保持平衡。 –

+0

@ErmIg出于好奇,你是否可以在不同的实现上做一些基准测试,如果是的话,他们是如何比较的? –

+0

不幸的是,我刚刚写下了这段代码。我没有检查过它的表现。 – ErmIg