作为 GPU 编程(以及某种 CMake)的初学者,我一直在尝试使用 CMake 和 CUDA 制作一个小项目,但我遇到了问题,所以我想知道一种执行以下操作的方法:
好吧,假设我有两个单独的文件(它们实际上分别是一个
.cuh
和一个 .cu
文件,但我只是将它们分别称为一个,因为它们被编译在一起)我有文件 A和文件 B.
我本质上想要这个:
文件 A 调用位于其自己文件中的
__global__
函数,该全局函数然后调用文件 B 中的 __device__
函数。
我希望能够将文件 B 编译为库,以便其他文件出于任何原因想要引用此
__device__
函数,并且我希望将文件 A 编译为可执行文件。
我该如何去做呢?将它们保存在同一个文件中是唯一的选择吗?
编辑:
我尝试添加
-dc
和 -rdc=true
,但还有其他没有 CUDA 功能(没有设备全局或主机功能)的文件正在被编译到库中并链接到文件 A。
编辑2:
我意识到我上传了之前测试中的错误,无论如何我有一个我正在做的事情的小版本,我试图保持简短,但这是文件结构
main.cu
main.cuh
CMakeLists.txt
Maths/
AMaths.cu
AMaths.cuh
GMaths.cu
GMaths.cuh
CMakeLists.txt
CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
project (Engine LANGUAGES CUDA CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -rdc=true -lcudadevrt -lcublas_device -dc")
include_directories(Maths)
add_executable(Main Main.cu Main.cuh)
add_subdirectory(Maths)
target_link_libraries(Main
PRIVATE
GMaths
AMaths)
主.cu
#include "Main.cuh"
__global__ void caller(float* vec1, float* vec2, float* out) {
int i = threadIdx.x + blockDim.x * blockIdx.x;
GMaths::Add(vec1[i], vec2[i], out[i]);
}
int main() {
float* vec1;
float* vec2;
float* out;
cudaMalloc(&vec1, sizeof(float) * 3);
cudaMalloc(&vec2, sizeof(float) * 3);
cudaMallocManaged(&out, sizeof(float) * 3);
float* temp = new float[3] {1, 2, 3};
cudaMemcpy(temp, &vec1, sizeof(float) * 3, cudaMemcpyHostToDevice);
cudaMemcpy(temp, &vec2, sizeof(float) * 3, cudaMemcpyHostToDevice);
caller <<< 1, 3 >>> (vec1, vec2, out);
return 0;
}
主.cuh
#ifndef MAIN_CUH
#define MAIN_CUH
#include "GMaths.cuh"
#include "AMaths.cuh"
__global__ void caller(float* vec1, float* vec2, float* out);
int main();
#endif
数学\AMaths.cu
#include "AMaths.cuh"
namespace AMaths {
void VecAdd(float* vec1, float* vec2, int vecsize, float* out) {
for (int i = 0; i < vecsize; i++) {
out[i] = vec1[i] + vec2[i];
}
}
float V2Dot(Vector2 v1, Vector2 v2) {
return v1.x * v2.x + v1.y * v2.y;
}
float V3Dot(Vector3 v1, Vector3 v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z *v2.z;
}
Quaternion EulerToQuaternion(Euler angles) {
Quaternion result(std::cos(angles.roll / 2) * std::cos(angles.pitch / 2) * std::cos(angles.yaw / 2) + std::sin(angles.roll / 2) * std::sin(angles.pitch / 2) * std::sin(angles.yaw / 2),
std::sin(angles.roll / 2) * std::cos(angles.pitch / 2) * std::cos(angles.yaw / 2) - std::cos(angles.roll / 2) * std::sin(angles.pitch / 2) * std::sin(angles.yaw / 2),
std::cos(angles.roll / 2) * std::sin(angles.pitch / 2) * std::cos(angles.yaw / 2) + std::sin(angles.roll / 2) * std::cos(angles.pitch / 2) * std::sin(angles.yaw / 2),
std::cos(angles.roll / 2) * std::cos(angles.pitch / 2) * std::sin(angles.yaw / 2) - std::sin(angles.roll / 2) * std::sin(angles.pitch / 2) * std::cos(angles.yaw / 2));
return result;
}
Euler QuaternionToEuler(Quaternion angles) {
float sinr_cosangles = 2 * (angles.w * angles.i + angles.j * angles.k);
float cosr_cosangles = 1 - 2 * (angles.i * angles.i + angles.j * angles.j);
float sinangles = std::sqrt(1 + 2 * (angles.w * angles.j - angles.i * angles.k));
float cosangles = std::sqrt(1 - 2 * (angles.w * angles.j - angles.i * angles.k));
float siny_cosangles = 2 * (angles.w * angles.k + angles.i * angles.j);
float cosy_cosangles = 1 - 2 * (angles.j * angles.j + angles.k * angles.k);
Euler result(std::atan2(siny_cosangles, cosy_cosangles),
2 * std::atan2(sinangles, cosangles) - PI / 2,
std::atan2(sinr_cosangles, cosr_cosangles));
return result;
}
void Vector3ToQuaternion(Vector3 vector, Quaternion &out) {
out.w = 0;
out.i = vector.x;
out.j = vector.y;
out.k = vector.z;
}
void QuaternionToVector3(Quaternion quat, Vector3 &out) {
out.x = quat.i;
out.y = quat.j;
out.z = quat.k;
}
void QuaternionMultiplication(Quaternion Q1, Quaternion Q2, Quaternion &out) {
out.w = Q1.w * Q2.w - Q1.i * Q2.i - Q1.j * Q2.j - Q1.k * Q2.k;
out.i = Q1.w * Q2.i + Q1.i * Q2.w + Q1.j * Q2.k - Q1.k * Q2.j;
out.j = Q1.w * Q2.j - Q1.i * Q2.k + Q1.j * Q2.w + Q1.k * Q2.i;
out.k = Q1.w * Q2.k + Q1.i * Q2.j - Q1.j * Q2.i + Q1.k * Q2.w;
}
void InverseQuaternion(Quaternion Q, Quaternion &out) {
out.w = Q.w;
out.i = -Q.i;
out.j = -Q.w;
out.k = -Q.k;
}
void QuaternionToRotationMatrix(Quaternion Q, RotationMatrix &out) {
float v01 = Q.w * Q.i;
float v02 = Q.w * Q.j;
float v03 = Q.w * Q.k;
float v12 = Q.i * Q.j;
float v13 = Q.i * Q.k;
float v23 = Q.j * Q.k;
float q02 = Q.w * Q.w;
out.x1 = 2 * (q02 + Q.i * Q.i) - 1;
out.y2 = 2 * (q02 + Q.j * Q.j) - 1;
out.z3 = 2 * (q02 + Q.k * Q.k) - 1;
out.y1 = 2 * (v12 - v03);
out.z1 = 2 * (v13 + v02);
out.x2 = 2 * (v12 + v03);
out.z2 = 2 * (v23 - v01);
out.x3 = 2 * (v13 - v02);
out.y3 = 2 * (v23 + v01);
}
}
数学\AMaths.cuh
#ifndef AMATHS_H
#define AMATHS_H
#include <cmath>
#include <iostream>
namespace AMaths {
#define PI 3.14159265359
}
namespace Literals {
constexpr long double operator"" _mm(long double x) { return x / 1000; };
constexpr long double operator"" _cm(long double x) { return x / 100; };
constexpr long double operator"" _m(long double x) { return x; };
constexpr long double operator"" _deg(long double x) { return x * (PI / 180); };
constexpr long double operator"" _rad(long double x) { return x; };
}
namespace AMaths {
#define HALFPI 1.57079632679
#define QUATERPI 0.785398163397
#define ONEOVERPI 0.318309886184
#define A 0.0776509570923569
#define B -0.287434475393028
#define C (QUATERPI - A - B)
#define S1 0.166666666667
#define S2 0.00833333333333
#define S3 0.000198412698413
struct Vector2 {
float x, y;
};
struct Vector3 {
float x, y, z;
Vector3() {
this->x = 0;
this->y = 0;
this->z = 0;
}
Vector3(float x, float y, float z) {
this->x = x;
this->y = y;
this->z = z;
}
};
struct Euler {
float yaw, pitch, roll;
Euler() {
this->yaw = 0;
this->pitch = 0;
this->roll = 0;
}
Euler(float yaw, float pitch, float roll) {
this->yaw = yaw;
this->pitch = pitch;
this->roll = roll;
}
};
struct Quaternion {
float w, i, j, k;
Quaternion() {
this->w = 0;
this->i = 0;
this->j = 0;
this->k = 0;
}
Quaternion(float w, float i, float j, float k) {
this->w = w;
this->i = i;
this->j = j;
this->k = k;
}
};
struct RotationMatrix {
float x1, y1, z1;
float x2, y2, z2;
float x3, y3, z3;
};
void VecAdd(float* vec1, float* vec2, int vecsize, float* out);
float V2Dot(Vector2 v1, Vector2 v2);
float V3Dot(Vector3 v1, Vector3 v2);
Quaternion EulerToQuaternion(Euler);
Euler QuaternionToEuler(Quaternion);
void Vector3ToQuaternion(Vector3 vector, Quaternion &out);
void QuaternionToVector3(Quaternion quat, Vector3 &out);
void QuaternionMultiplication(Quaternion Q1, Quaternion Q2, Quaternion &out);
void InverseQuaternion(Quaternion Q, Quaternion &out);
void QuaternionToRotationMatrix(Quaternion Q, RotationMatrix &out);
}
#endif
数学\GMaths.cu
#include "GMaths.cuh"
namespace GMaths {
__device__ void Add(float val1, float val2, float &out) {
out = val1 + val2;
}
__global__ void VecAdd(float* vec1, float* vec2, float* out) {
int i = threadIdx.x + blockDim.x * blockIdx.x;
out[i] = vec1[i] + vec2[i];
}
__global__ void MatMulVec4(float* matrix[16], float* vector[4], float* out[4]) {
int temp = threadIdx.x + blockDim.x * blockIdx.x;
int i = temp % 4;
int s = (int)(temp / (4));
out[s][i] = matrix[s][i] * vector[s][0] + matrix[s][i+4] * vector[s][1] + matrix[s][i+8] * vector[s][2] + matrix[s][i+12] * vector[s][3];
}
}
数学\GMaths.cuh
#ifndef GMATHS_CUH
#define GMATHS_CUH
namespace GMaths {
__device__ void Add(float val1, float val2, float &out);
__global__ void VecAdd(float* vec1, float* vec2, float* out);
__global__ void MatMulVec4(float* matrix[16], float* vector[4], float* out[4]);
}
#endif
数学\CMakeLists.txt
add_library(AMaths AMaths.cuh AMaths.cu)
add_library(GMaths GMaths.cuh GMaths.cu)
target_compile_features(AMaths PUBLIC cxx_std_11)
target_compile_features(GMaths PUBLIC cxx_std_11)
set_target_properties(AMaths PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
set_target_properties(GMaths PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
编辑3:
我当前遇到的具体错误:
Main.obj : error LNK2019: unresolved external symbol __cudaRegisterLinkedBinary_5bf867cd_7_Main_cu_cd620806 referenced in f
unction "void __cdecl __sti____cudaRegisterAll(void)" (?__sti____cudaRegisterAll@@YAXXZ) [C:\Users\{myname}\source\repos\Render
ingengine\build\Renderingengine\Main.vcxproj]
编辑4:
采纳paleonix的建议并删除标志并将
CUDA SEPERABLE COMPILATION
添加到main后,它就可以工作了
之前的CMakeLists.txt:
cmake_minimum_required (VERSION 3.8)
project (Engine LANGUAGES CUDA CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -rdc=true -lcudadevrt -lcublas_device -dc")
include_directories(Maths)
add_executable(Main Main.cu Main.cuh)
add_subdirectory(Maths)
target_link_libraries(Main
PRIVATE
GMaths
AMaths)
CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
project (Engine LANGUAGES CUDA CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -rdc=true -lcudadevrt -lcublas_device -dc")
include_directories(Maths)
add_executable(Main Main.cu Main.cuh)
add_subdirectory(Maths)
target_link_libraries(Main
PRIVATE
GMaths
AMaths)
之后:
cmake_minimum_required (VERSION 3.8)
project (Engine LANGUAGES CUDA CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CUDA_STANDARD 17)
include_directories(Maths)
add_executable(Main Main.cu Main.cuh)
set_target_properties(Main PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
add_subdirectory(Maths)
target_link_libraries(Main
PRIVATE
GMaths
AMaths)
非常抱歉,这有效,我忘记删除我的示例的其他部分
将各种flag放入
CMAKE_CUDA_FLAGS
就像
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -rdc=true -lcudadevrt -lcublas_device -dc")
是一个坏主意,因为即使它有效,它也不是很可移植(例如,如果您想使用 clang 作为 CUDA 编译器,它就不会工作)。在这种特殊情况下,您还混合了编译器标志和链接器标志,这可能是链接器错误的实际原因。
要替换
-rdc=true
和 -dc
,只需在 CUDA_SEPARABLE_COMPILATION
目标上使用 Main
属性,就像对这两个库所做的那样。根据我的观察,-lcudadevrt
是由CMake自动添加的,不需要您的输入。 -lcublas_device
有点棘手,因为 CUDAToolkit
包为 cuBLAS 和其他 CUDA 库提供目标,但似乎没有为设备端 cuBLAS 库提供目标。理想情况下,人们会为此创建一个适当的目标,但在您的上下文中,在 cublas_device
命令中使用普通名称 target_link_libraries()
可能就足够了。
补充说明:
cxx_std_11
不适用于 CUDA
目标,您应该使用 CMake 3.17 中提供的 cuda_std_11
,即 cmake_minimum_required()
应进行相应修改。