我有以下代码来计算Hadamard变换。目前,hadamard函数是我程序的瓶颈。您看到任何加速的潜力吗?也许使用AVX2指令?典型的输入大小约为512或1024。
最好,汤姆
#include <stdio.h>
void hadamard(double *p, size_t len) {
double tmp = 0.0;
if(len == 2) {
tmp = p[0];
p[0] = tmp + p[1];
p[1] = tmp - p[1];
} else {
hadamard(p, len/2);
hadamard(p+len/2, len/2);
for(int i = 0; i < len/2; i++) {
tmp = p[i];
p[i] = tmp + p[i+len/2];
p[i+len/2] = tmp - p[i+len/2];
}
}
}
int main(int argc, char* argv[]) {
double a[] = {1.0, 2.0, 3.0, 4.0};
hadamard(a, 4);
}
答案 0 :(得分:3)
这是基于Fast Walsh–Hadamard transform的概念验证实现,具有优化的第一遍。使用clang-3.3或更高版本可正常编译,并使用clang-4.0 or later可获得不错的结果(需要-O2才能正确内联相关函数)。如果没有FMA,则需要在hada2_
中将-0.0
的下两个元素与hadamard4
进行异或(并使用普通的_mm256_add_pd
)。
我检查的所有gcc版本都需要通过手动加载/存储内部函数替换memcpy
才能获得相似的结果。
我还保留了对案例len<16
的处理。为了更好地使用可用寄存器并减少内存访问,可能值得实现hadamard32
,并且可能类似于hadamard64
的{{1}}。在C ++中,这可以通过递归模板实现来完成。
hadamard16
基准测试也作为练习;-)
免责声明:我没有对此进行测试。来看一下,我可能在#include <immintrin.h> // avx+fma
#include <assert.h> // assert
#include <string.h> // memcpy
inline __m256d hadamard4(__m256d x0123)
{
__m256d x1032 = _mm256_permute_pd(x0123, 5); // [x1, x0, x3, x2]
__m256d hada2 = _mm256_addsub_pd(x1032,x0123); // [x0+x1, x0-x1, x2+x3, x2-x3]
__m256d hada2_= _mm256_permute2f128_pd(hada2, hada2, 1); // [x2+x3, x2-x3, x0+x1, x0-x1]
// if no FMA is available, this can be done with xoring and adding:
__m256d res = _mm256_fmadd_pd(hada2_, _mm256_set_pd(1.0, 1.0, -1.0, -1.0), hada2);
return res;
}
inline void hadamard8(__m256d data[2])
{
__m256d a = hadamard4(data[0]);
__m256d b = hadamard4(data[1]);
data[0] = _mm256_add_pd(a,b);
data[1] = _mm256_sub_pd(a,b);
}
inline void hadamard16(__m256d data[4])
{
hadamard8(data+0);
hadamard8(data+2);
for(int i=0; i<2; ++i)
{
__m256d tmp = data[i];
data[i] = _mm256_add_pd(tmp, data[i+2]);
data[i+2]= _mm256_sub_pd(tmp, data[i+2]);
}
}
void hadamard(double* p, size_t len)
{
assert((len&(len-1))==0); // len must be power of 2
assert(len>=16); // TODO implement fallback for smaller sizes ...
// first pass: hadamard of 16 values each
for(size_t i=0; i<len; i+=16)
{
__m256d data[4];
memcpy(data, p+i, sizeof(data)); // should get optimized to 4x vmovupd
hadamard16(data);
memcpy(p+i, data, sizeof(data)); // should get optimized to 4x vmovupd
}
for(size_t h=32; h<len; h*=2)
{
for(size_t i=0; i<len; i+=2*h)
{
for(size_t j=i; j<i+h; j+=4)
{
__m256d x = _mm256_loadu_pd(p+j);
__m256d y = _mm256_loadu_pd(p+j+h);
_mm256_storeu_pd(p+j, _mm256_add_pd(x,y));
_mm256_storeu_pd(p+j+h, _mm256_sub_pd(x,y));
}
}
}
}
指令中混合了hada2_
和hada2
...