12

我有一个定义为的函数

inline void vec_add(__m512d &v3, const __m512d &v1, const __m512d &v2) {
    v3 = _mm512_add_pd(v1, v2);
}

(这__m512d是在英特尔 MIC 架构上映射到 SIMD 寄存器的本机数据类型)

由于这个函数相当短并且经常被调用,我希望它在每次调用时都被内联。但是英特尔的编译器似乎不愿意内联这个函数,即使在我使用了-inline-forceinlineand-O3选项之后。它在编译时报告“Forceinline 不接受调用 ...”。由于我必须使用一些编译器特定的功能,例如__m512d类型,英特尔编译器是我唯一的选择。

更多信息:

文件结构非常简单。该函数vec_add在一个头文件中定义,该文件mic.h包含在另一个文件中test.cc。函数vec_add只是在循环中重复调用,不涉及函数指针。代码的简化版本test.cc如下所示

for (int i = 0; i < LENGTH; i += 8) {
    // a, b, c are arrays of doubles, and each SIMD register can hold 8 doubles
    __mm512d va = _mm512_load_pd(a + i); // load SIMD register from memory
    __mm512d vb = _mm512_load_pd(b + i); // ditto
    __mm512d vc;
    vec_add(vc, va, vb); // store SIMD register to memory
    _mm512_store_pd(c + i, vc);
}

我尝试了各种提示,例如__attribute__((always_inline)),__forceinline和 compiler option -inline-forceinline,但都没有奏效。

完整代码

我以简化的形式将所有相关代码放在一起。如果您有英特尔编译器,您可以尝试一下。使用选项-Winline查看内联报告并-inline-forceinline强制内联。

#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>

#define LEN (1<<20)

__attribute((target(mic)))
inline void vec_add(__m512d &v3, const __m512d &v1, const __m512d &v2) {
    v3 = _mm512_add_pd(v1, v2);
}

int main() {
    #pragma offload target(mic)
    {
        double *a = (double*)_mm_malloc(LEN*sizeof(double), 64);
        double *b = (double*)_mm_malloc(LEN*sizeof(double), 64);
        double *c = (double*)_mm_malloc(LEN*sizeof(double), 64);

        for (int i = 0; i < LEN; i++) {
            a[i] = (double)rand()/RAND_MAX;
            b[i] = (double)rand()/RAND_MAX;
        }

        for (int i = 0; i < LEN; i += 8) {
            __m512d va = _mm512_load_pd(a + i);
            __m512d vb = _mm512_load_pd(b + i);
            __m512d vc;
            vec_add(vc, va, vb);
            _mm512_store_pd(c + i, vc);
        }

        _mm_free(a);
        _mm_free(b);
        _mm_free(c);
    }
}

配置

  • 编译器:英特尔编译器(ICC)14.0.2
  • 编译选项:-O3 -inline-forceinline -Winline

你知道为什么这个函数不能被内联吗?毕竟我怎样才能让它内联(我不想转向宏)?

4

1 回答 1

9

出于某种原因,英特尔编译器不会在卸载代码中内联函数(我对这个概念并不十分熟悉,所以我不知道这是什么技术原因)。有关更多信息,请参阅有效使用英特尔编译器卸载功能(只需搜索“内联”)。

引用链接的文章:

函数内联到卸载构造

有时内联函数对于生成的代码的最佳性能是必要的。在 #pragma offload 中直接调用的函数 不会被编译器内联,即使它们被标记为 inline。为了在卸载区域中实现代码的最佳性能,可以手动内联函数,或者将整个卸载构造放入自己的函数中。

...

一种解决方案是手动内联函数 f,如函数 v2 所示。

另一种解决方案是将卸载构造移动到它自己的函数中,如函数 v3 所示。

如果我理解正确,对您来说最好的办法是将循环放入一个单独的函数中,该函数也标有 __attribute((target(mic)))。

于 2014-05-15T13:02:16.280 回答