使用CUDA编译失败:异常规范与前一个函数 "rsqrt" 不兼容
我在我的机器上安装了手动版的CUDA 13.1,运行Devuan GNU/Linux Freia。最近更新发行版的软件包后,包含一些CUDA头文件的C++程序的编译失败,错误信息如下:
/usr/include/x86_64-linux-gnu/bits/mathcalls.h(206): error: exception
specification is incompatible with that of previous function "rsqrt"
(declared at line 629 of
/usr/local/cuda-13.1/bin/../targets/x86_64-linux/include/crt/math_functions.h)
extern double rsqrt (double __x) noexcept (true); extern double __rsqrt (double __x) noexcept (true);
在 mathcalls.h 中,我看到那一行写着:
__MATHCALL (rsqrt,, (_Mdouble_ __x));
它展开为:
extern double rsqrt(double __x) noexcept (true);
extern double __rsqrt(double __x) noexcept (true);
而在 /usr/local/cuda-13.1/bin/../targets/x86_64-linux/include/crt/math_functions.h 中,我有:
extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double rsqrt(double x);
我猜这个宏展开成一个 noexcept(true),还是某种东西,与NVIDIA在 CUDA头文件中的定义不兼容。
我尝试把默认编译器从发行版中的版本15换成GCC 14,或13,但都没用。
- 为什么会这样?我知道很可能是发行版的软件包升级造成的,但究竟是为什么呢?
- 我应该如何规避这个问题?
备注:
- 这在CUDA 13.2上不会发生
- 这让我想起了一个 类似的问题,我(以及大家)在某些三角函数的异常规范上遇到的情况,比如
__sinpi()和__cospi();我希望不必再次对CUDA进行打补丁 :-(
解决方案
tl;dr:你需要对CUDA头文件进行打补丁 :-(
为什么会这样?
较新版本的glibc(你的版本是2.42)提供了越来越多过去不可用的数学函数,其中一些也以CUDA __device__ 函数的形式出现在CUDA头文件中。去年是 sinpi()、cospi() 等,今年是 rsqrt() 和 rsqrtf()。基本签名是相同的,但函数的声明可能是抛出异常的,或是不抛出异常的,取决于……等因素,而CUDA发行版必须使用匹配的声明。
通过对宏的处理,可以让所有GCC版本实现这种匹配,但NVIDIA只处理了他们已经知道的声明冲突。对于额外的函数,我们必须自己动手——通过补丁来实现。
致谢 @mohangk和 @morgwai666在 NVIDIA开发者论坛上的贡献,他们提供了两种替代补丁。
当你当前目录位于 /usr/local/cuda/include/crt(或在 crt/ 子目录中的CUDA include目录)时,必须同时应用这两种补丁。
针对CUDA 13.0及更高版本的补丁
较新版本的CUDA已经意识到glibc的问题 此补丁 适用于CUDA的较新版本,并使用基于宏的机制来控制是否将某些数学函数定义为抛出异常:
--- math_functions.h 2026-03-07 12:36:19.092019479 +0100
+++ math_functions.h 2026-03-07 17:15:33.322027903 +0100
@@ -604,6 +604,11 @@ extern __DEVICE_FUNCTIONS_DECL__ __devic
#if defined(__QNX__) && !defined(_LIBCPP_VERSION)
} /* std */
#endif
+#if __NV_GLIBC_PROVIDES_IEC_60559_FUNCS
+#define __NV_IEC_60559_FUNCS_EXCEPTION_SPECIFIER __THROW
+#else
+#define __NV_IEC_60559_FUNCS_EXCEPTION_SPECIFIER
+#endif
/**
* \ingroup CUDA_MATH_DOUBLE
* \brief Calculate the reciprocal of the square root of the input argument.
@@ -626,7 +631,7 @@ extern __DEVICE_FUNCTIONS_DECL__ __devic
*
* \note_accuracy_double
*/
-extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double rsqrt(double x);
+extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double rsqrt(double x) __NV_IEC_60559_FUNCS_EXCEPTION_SPECIFIER;
/**
* \ingroup CUDA_MATH_SINGLE
@@ -650,7 +655,8 @@ extern __DEVICE_FUNCTIONS_DECL__ __devic
*
* \note_accuracy_single
*/
-extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float rsqrtf(float x);
+extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float rsqrtf(float x) __NV_IEC_60559_FUNCS_EXCEPTION_SPECIFIER;
+#undef __NV_IEC_60559_FUNCS_EXCEPTION_SPECIFIER
#if defined(__QNX__) && !defined(_LIBCPP_VERSION)
namespace std {
针对CUDA 12.* 及更早版本的补丁
这是一个“更丑”的补丁,可能仅对较新版本的glibc有效?:
--- a/math_functions.h
+++ b/math_functions.h
@@ -626,7 +626,7 @@
*
* \note_accuracy_double
*/
-extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double rsqrt(double x);
+extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ double rsqrt(double x) noexcept (true);
/**
* \ingroup CUDA_MATH_SINGLE
@@ -650,7 +650,7 @@
*
* \note_accuracy_single
*/
-extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float rsqrtf(float x);
+extern __DEVICE_FUNCTIONS_DECL__ __device_builtin__ float rsqrtf(float x) noexcept (true);
#if defined(__QNX__) && !defined(_LIBCPP_VERSION)
namespace std {