尝试从另一个文件的全局函数调用设备函数

问题描述 投票:0回答:1

作为 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)

非常抱歉,这有效,我忘记删除我的示例的其他部分

c++ cmake cuda
1个回答
1
投票

将各种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()
    应进行相应修改。
© www.soinside.com 2019 - 2024. All rights reserved.