如何有效地链接avx2内在函数来执行算术运算链?

问题描述 投票:0回答:1

我编写了一个大型程序来模拟分子系统。我在一台台式计算机上运行它,其处理器是 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 optimization vectorization intrinsics avx2
1个回答
0
投票

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

如果您想对此进行优化,请首先查看编译器正在做什么。这真是太聪明了。

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