当在两台不同的机器上构建我的大学项目时,我遇到了 LAPACK 和 Intel 在 MKL 中的实施问题。我已经找出了主要问题:我使用了
liblapacke-devel
中的 LAPACK 标头,这在两台机器之间有所不同。在其中一个标头中,每个 LAPACK 函数在参数列表末尾都有两个额外的 size_t
参数,其中包含某项。与 LAPACK 的 Fortran 起源有关。意识到这一点,并且由于我无论如何都链接到 MKL,所以我决定使用 MKL 标头,它们在参数列表中没有区别。
但是我很好奇,并尝试使用附加参数调用 LAPACK 函数之一 (
dsyev
),但仍然链接到 MKL,而 MKL 没有这些附加参数。我预计会出现段错误或类似错误,但我的测试程序运行得很好。可能是什么原因?
我的理论是,由于参数位于参数列表的末尾,因此当返回调用函数时它们不会被访问和清理。如果属实,这是否意味着做这样的事情就可以了,或者是否存在其他可能导致运行时问题的影响?在调用
dsyev
之前如何检查可能未对齐的堆栈?
我尝试了
objdump
和nm
,却发现有关参数列表的信息从来都不是共享库的一部分,而只是符号名称的一部分。由于我缺乏使用 GDB
的经验,我也无法从那里提取任何有意义的信息。我怎样才能知道我的理论为什么仍然有效?
以下是
lapack.h
和 mkl_lapack.h
引用 dsyev_
的相关摘录,来自两个装置:
容器,来自 lapack.h
的 liblapacke-dev
,版本 3.10.0-2ubuntu1
:
...
/* It seems all current Fortran compilers put strlen at end.
* Some historical compilers put strlen after the str argument
* or make the str argument into a struct. */
#define LAPACK_FORTRAN_STRLEN_END
...
#define LAPACK_dsyev_base LAPACK_GLOBAL(dsyev,DSYEV)
void LAPACK_dsyev_base(
char const* jobz, char const* uplo,
lapack_int const* n,
double* A, lapack_int const* lda,
double* W,
double* work, lapack_int const* lwork,
lapack_int* info
#ifdef LAPACK_FORTRAN_STRLEN_END
, size_t, size_t
#endif
);
#ifdef LAPACK_FORTRAN_STRLEN_END
#define LAPACK_dsyev(...) LAPACK_dsyev_base(__VA_ARGS__, 1, 1)
#else
#define LAPACK_dsyev(...) LAPACK_dsyev_base(__VA_ARGS__)
#endif
这会导致
dsyev
有两个额外的 size_t
参数,然后再次由 LAPACK_dsyev
定义处理。
主持人,
lapack.h
来自liblapacke-dev
,版本3.9.0-1build1
:
#define LAPACK_dsyev LAPACK_GLOBAL(dsyev,DSYEV)
void LAPACK_dsyev(
char const* jobz, char const* uplo,
lapack_int const* n,
double* A, lapack_int const* lda,
double* W,
double* work, lapack_int const* lwork,
lapack_int* info );
容器,
mkl_lapack.h
,来自 Intel OneMKL
,版本 2024.2
:
void DSYEV( const char* jobz, const char* uplo, const MKL_INT* n, double* a,
const MKL_INT* lda, double* w, double* work, const MKL_INT* lwork,
MKL_INT* info ) NOTHROW;
void dsyev( const char* jobz, const char* uplo, const MKL_INT* n, double* a,
const MKL_INT* lda, double* w, double* work, const MKL_INT* lwork,
MKL_INT* info ) NOTHROW;
void dsyev_( const char* jobz, const char* uplo, const MKL_INT* n, double* a,
const MKL_INT* lda, double* w, double* work, const MKL_INT* lwork,
MKL_INT* info ) NOTHROW;
(Capslock C 风格、C 风格、Fortran 风格)
主持人,
mkl_lapack.h
来自Intel OneMKL
,版本2021.1.1
:
void DSYEV( const char* jobz, const char* uplo, const MKL_INT* n, double* a,
const MKL_INT* lda, double* w, double* work, const MKL_INT* lwork,
MKL_INT* info ) NOTHROW;
void DSYEV_( const char* jobz, const char* uplo, const MKL_INT* n, double* a,
const MKL_INT* lda, double* w, double* work, const MKL_INT* lwork,
MKL_INT* info ) NOTHROW;
void dsyev( const char* jobz, const char* uplo, const MKL_INT* n, double* a,
const MKL_INT* lda, double* w, double* work, const MKL_INT* lwork,
MKL_INT* info ) NOTHROW;
void dsyev_( const char* jobz, const char* uplo, const MKL_INT* n, double* a,
const MKL_INT* lda, double* w, double* work, const MKL_INT* lwork,
MKL_INT* info ) NOTHROW;
(Capslock C 风格、Capslock Fortran 风格、C 风格、Fortran 风格)
我预计会出现段错误或类似错误,但我的测试程序运行得很好。
您的期望是不正确的。在这种情况下,工作正常是预期的行为。
可能是什么原因?
如果您阅读了有关调用约定的内容,您就会明白额外的参数是在附加寄存器(或堆栈上)中传递的。
调用者将这些参数放在那里,但被调用的例程只是从不查看它们(因为它不期望它们)。
因此唯一的影响是调用者执行不必要的操作并产生微小的开销。