英特尔 MKL - mkl_intel_lp64 和 mkl_gf_lp64 之间的区别

计算科学 布拉斯 编译
2021-11-29 06:55:59

我目前正在尝试将程序链接到英特尔 MKL 11.0 库,而不是使用 NetLIB 或 OpenBLAS。这样做我发现了以下错误,目前我无法向自己解释。考虑以下 C 代码示例,使用 计算复杂的标量积zdotc

#include <stdio.h>
#include <stdlib.h>
#include <complex.h>
double complex zdotc_(int *n, double complex *X, int *incx, double complex *Y, int *INCY ); 
int main ( ) {
    int n = 5; 
    int incx = 1, incy = 1; 
    double complex x[5] = {1,I,2,2+I,3}; 
    double complex y[5] = {I,3,I*3, 2+2*I, 9}; 
    double complex ret; 
    ret = zdotc_(&n,x,&incx,y,&incy); 
    printf("n   = %d\n", n); 
    printf("ret = %lg + %lgi\n", creal(ret), cimag(ret)); 
    return 0; 
}

我使用MKL Advisor给出的命令行标志编译了这个例子我选择“GNU C/C++、32 位整数、动态链接、GNU OpenMP”。生成的命令行是:

gcc zdotc_test.c -o zdot_mkl_gcc -O2  -L$MKLROOT/lib/intel64 -lmkl_intel_lp64 -lmkl_gnu_thread -lmkl_core -ldl -lpthread -lm  -fopenmp -m64 -I$MKLROOT/include

这个程序的输出是:

n   = 0
ret = 0 + 1.07933e+21i

这显然是错误的,尤其是为什么要改变n

如果我选择 GNU Fortran 而不是 GNU C/C++,我必须替换-lmkl_intel_lp64by-lmkl_gf_lp64然后正确的输出

n   = 5
ret = 33 + 6i

被生产。

所以我的问题是:接口之间的详细区别在哪里,为什么第一个会产生这个错误?

1个回答

差异与调用约定 (ABI) 差异有关。ZDOTC是一个有问题的函数,因为它返回 a double complex,它通常被认为是结构而不是“简单”数据类型。因此,它既可以作为返回值返回堆栈,也可以通过引用作为“C”风格函数的隐式第一个参数。我猜你在这里看到的是,在这种_intel_情况下,它期望返回值是隐式的第一个参数,所以它返回值并最终修改 n,然后返回值完全错误。在这种情况下,正确的声明是

void zdotc_(double complex *retval, int *n, double complex *X, int *incx, double complex *Y, int *INCY );

像这样的愚蠢的东西让我尽可能在 C++ 中实现所有东西。特别是,我倾向于在 C++ 中实现 BLAS 级别 1,其中性能无论如何都会受到内存带宽的限制。对于 BLAS 级别 2 和 3,我将调用实际的 BLAS,它没有解决此问题的功能。

我相信在比较天真的点积实现与优化的 BLAS 之前,我已经看过基准测试,如果您使用__restrict类型关键字,大多数优化编译器将生成与优化的 BLAS 一样快的代码。

我自己的版本位于此处,您需要相应的源文件来提供更高级别的 BLAS 的转发,位于此处我会向您推荐 Eigen,但我的代码更容易移植到 C99,因为它的模板化程度较低。