nvcc 为何提示 thrust::complex 元素没有 real() 和 imag()?
先把 data[0] 读取为 thrust::complex<float> 类型的对象,再调用 real() 和 imag()。报错中的对象类型是 thrust::device_reference<…>,它没有这两个成员;thrust::complex<float> 本身有这两个成员函数。
为什么 device_vector 会报错?
在主机端通过 operator[] 访问设备内存时:
data[0]
得到的是代理对象 thrust::device_reference<thrust::complex<float>>。它支持读写设备元素,但不是普通的 thrust::complex<float>&,也不会自动暴露被代理对象的所有成员函数。
下面的表达式会在代理类型上查找 real():
data[0].real()
C++ 的 . 成员访问不会先将代理对象隐式转换为复数对象,再查找其成员,因此编译器报告 device_reference 没有 real 和 imag。
改用 thrust::host_vector 后,元素位于主机内存中,可以通过普通元素引用访问 thrust::complex<float> 的成员,相同的调用也就能够编译。
如何修改代码?
打印前,将设备元素读取到一个主机端复数变量中:
thrust::complex<float> value = data[0];
printf("data[0] = %f %fi\n", value.real(), value.imag());
完整示例:
#include <stdio.h>
#include <thrust/complex.h>
#include <thrust/device_vector.h>
int main() {
thrust::device_vector<thrust::complex<float>> data(1);
data[0] = thrust::complex<float>(1.0f, 2.0f);
data[0] *= 5.0f;
thrust::complex<float> value = data[0];
printf("data[0] = %f %fi\n", value.real(), value.imag());
return 0;
}
按示例中的数值计算,(1 + 2i) × 5 = 5 + 10i,预期输出为:
data[0] = 5.000000 10.000000i
为什么赋值和乘法可以使用?
device_reference 支持设备元素访问所需的相应赋值和运算符,因此可以通过代理执行:
data[0] = thrust::complex<float>(1.0f, 2.0f);
data[0] *= 5.0f;
支持这些运算符并不意味着代理类型具有 thrust::complex 的全部成员函数。能否调用 .real(),取决于表达式本身的类型。
使用时要注意什么?
不要把读取语句改成下面这样:
auto value = data[0];
这里的 auto 会推导出代理类型,调用 value.real() 仍会遇到相同问题。使用明确的 thrust::complex<float> 类型,才能取得复数值对象。
从主机端读取设备元素涉及设备到主机的数据传输。用于调试时,可以这样读取单个元素;如果需要处理大量元素,应考虑批量复制到主机,或在设备端完成计算,避免在主机循环中逐个读取。
现有错误信息已经能定位到代理类型的成员访问问题,无需先调整 WSL、Ubuntu 或 CUDA toolkit 配置。
备注:内容仅供参考。
