我编写了一个大型程序来模拟分子系统。我在一台台式计算机上运行它,其处理器是 Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz。大多数时间 (75%) 用于计算 4 个邻居的 Lennard Jones 势。
为了优化它,我正在学习如何使用
avx2 intrinsics
并编写了一个仅执行 LJ 计算的简化版本:(d_eq/d_cur)**12 - 2.0*(d_eq/d_cur)**6
这是没有内在函数的代码:
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
double lj_pot(double* d_cur) {
const double d_eq = 1.0;
double ratio, vpot1, eie;
eie =0.0;
for (int i=0; i<4; i++) {
ratio = d_eq/d_cur[i] ;
vpot1 = ratio*ratio*ratio ;
vpot1 = vpot1*vpot1 ;
eie += vpot1*(vpot1-2.0);
}
return eie;
}
int main()
{
double d_cur[4];
double eee;
eee=0.0;
for (int i=0; i<10000000; i++) {
for (int j=0; j<4; j++) {
d_cur[j] = 0.5+(double)(rand()) / (double)(RAND_MAX); // between 0.5 , 1.5
}
eee += lj_pot(d_cur);
}
printf("%f\n",eee);
return 0;
}
这是带有 AVX2 内在函数的代码
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
static inline double hsum_double_avx(__m256d v) {
// From https://stackoverflow.com/a/49943540/7462275
__m128d vlow = _mm256_castpd256_pd128(v);
__m128d vhigh = _mm256_extractf128_pd(v, 1); // high 128
vlow = _mm_add_pd(vlow, vhigh); // reduce down to 128
__m128d high64 = _mm_unpackhi_pd(vlow, vlow);
return _mm_cvtsd_f64(_mm_add_sd(vlow, high64)); // reduce to scalar
}
double lj_pot(double* d_cur) {
const __m256d d_eq = _mm256_set1_pd(1.0);
const __m256d two_dbl = _mm256_set1_pd(2.0);
__m256d vec1, vec2;
vec1 = _mm256_loadu_pd(d_cur);
vec1 = _mm256_div_pd(d_eq,vec1);
vec2 = _mm256_mul_pd(vec1,vec1); // (d_eq/d_cur)**2
vec1 = _mm256_mul_pd(vec2,vec2); // (d_eq/d_cur)**4
vec1 = _mm256_mul_pd(vec1,vec2); // (d_eq/d_cur)**6
vec2 = _mm256_sub_pd(vec1,two_dbl); // (d_eq/d_cur)**6 - 2.0
vec1 = _mm256_mul_pd(vec1,vec2); // (d_eq/d_cur)**12 -2.0*(d_eq/d_cur)**6
return hsum_double_avx(vec1);
}
int main()
{
double d_cur[4];
double eee;
eee=0.0;
for (int i=0; i<10000000; i++) {
for (int j=0; j<4; j++) {
d_cur[j] = 0.5+(double)(rand()) / (double)(RAND_MAX); // between 0.5 , 1.5
}
eee += lj_pot(d_cur);
}
printf("%f\n",eee);
return 0;
}
此代码输出与第一个代码相同的结果。但速度较慢。 (
gcc -O3 -mavx -o lj_pot lj_pot.c
用于编译这两个程序。)
当我检查汇编代码时,我想(也许这不是充分的理由)我没有正确使用 AVX2 内在函数来执行算术运算链。我必须改变什么才能让程序更快?
谢谢你的回答
GCC 自动使用 SIMD 作为您的原始代码。您的手动优化只会抑制编译器的优化。使用 -O3 -march=skylake -funsafe-math-optimizations
检查
生成的组件:
lj_pot(double*):
vbroadcastsd ymm1, QWORD PTR .LC1[rip]
vdivpd ymm1, ymm1, YMMWORD PTR [rdi]
vmulpd ymm0, ymm1, ymm1
vmulpd ymm0, ymm0, ymm1
vbroadcastsd ymm1, QWORD PTR .LC3[rip]
vmulpd ymm0, ymm0, ymm0
vaddpd ymm1, ymm0, ymm1
vmulpd ymm0, ymm1, ymm0
vextractf128 xmm1, ymm0, 0x1
vaddpd xmm1, xmm1, xmm0
vunpckhpd xmm0, xmm1, xmm1
vaddpd xmm0, xmm0, xmm1
vzeroupper
ret
如果您想对此进行优化,请首先查看编译器正在做什么。这真是太聪明了。