Cuda内核相当于一个金属计算内核

Cuda内核相当于一个金属计算内核,cuda,metal,Cuda,Metal,我有一个简单的金属计算内核,我正试图使Cuda等效。金属内核源代码是 #include <metal_stdlib> using namespace metal; constant uint stride [[function_constant(0)]]; constant float dt [[function_constant(1)]]; constant float a [[function_constant(2)]]; constant float b

我有一个简单的金属计算内核,我正试图使Cuda等效。金属内核源代码是

#include <metal_stdlib>
using namespace metal;

constant uint stride [[function_constant(0)]];
constant float dt    [[function_constant(1)]];
constant float a     [[function_constant(2)]];
constant float b     [[function_constant(3)]];

float2 f(const float2 x) {
    return float2(a, -b)*x.yx;
}

kernel void harmonic_occilator_stride(device float2 *x [[buffer(0)]],
                                             uint    i [[thread_position_in_grid]]) {
    for (uint j = 0; j < stride; j++) {
        x[i] += dt*f(x[i]);
    }
}
#包括
使用金属;
常数单位跨步[[函数单位常数(0)];
常数浮点dt[[函数_常数(1)];
常数浮点a[[函数_常数(2)];
常数浮点b[[函数_常数(3)];
浮动2 f(常数浮动2 x){
返回浮点数2(a,-b)*x.yx;
}
内核空谐振荡器跨步(设备浮动2*x[[缓冲区(0)],
uint i[[螺纹位置在网格中]]{
对于(uint j=0;j
我第一次尝试将其转换为Cuda,结果在编译ptx文件时出现了一系列错误

__constant__ uint  stride;
__constant__ float dt;
__constant__ float a;
__constant__ float b;

__device__ float2 f(const float2 x) {
    return float2(a, -b)*x.yx;
}

extern "C" __global__ void harmonic_occilator_stride(float2 *x) {
    size_t i = blockIdx.x*blockDim.x + threadIdx.x;
    for (uint j = 0; j < stride; j++) {
        x[i] += dt*f(x[i]);
    }
}
\uuuuu常量\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
__恒定浮点数;
__恒定浮点数a;
__恒定浮点数b;
__设备浮动2 f(常数浮动2 x){
返回浮点数2(a,-b)*x.yx;
}
外部“C”\uuuu全局\uuuuuu无效谐波\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu{
尺寸i=块IDX.x*块尺寸x+螺纹IDX.x;
对于(uint j=0;j
它不喜欢的第一件事是
x.yx
。在金属中,这与浮动2的内容顺序相反。如何在Cuda中反转或更改向量的访问顺序

接下来它也不喜欢
float2(a,-b)
。这会给出一个错误“没有合适的构造函数在
float
float2
之间进行转换”。如何构造向量文字

它抱怨的最后一件事是,
float
没有
*
操作符,而
dt*f(x[i])
行没有
float2
。如果我删除
dt*
并将其设置为
x[I]+=f(x[I])
它会抱怨
+=
没有
操作符用于
float2
float2
。如何对这些类型执行操作,以及如何对向量和标量进行乘法


在Metal中,当我将函数定义为
函数\常量时,Metal内核编译器将在运行时加载内核函数时JIT内核的特定优化版本。Cuda有这个功能吗?

我现在只浏览了一下。我不打算完全回答你的最后一个问题。但我认为,语法问题可以通过处理各种组件,并遵循metal定义的算术规则从概念上得到回答

它也不喜欢float2(a,-b)。这会产生一个错误“没有合适的构造函数在float和float2之间转换”。如何构造向量文字

为此,请使用头文件vector_functions.h(或.hpp)中定义的函数。(参见下面的示例)在vector_types.h中为CUDA定义的向量类型没有构造函数

它不喜欢的第一件事是x.yx。在金属中,这与浮动2的内容顺序相反。如何在Cuda中反转或更改向量的访问顺序

CUDA没有这种内置的多矢量元素处理/滑动功能。只需使用元素类型对元素执行操作

metal:  return float2(a, -b)*x.yx;

CUDA:   #include <vector_functions.h>
        ...
        return make_float2(a*x.y, -b*x.x);
如果您想这样做,应该可以定义一组您自己的向量类型,以匹配金属的大部分功能。我在这里描述的是“内置的”,如果您想用构造函数、算术运算符等创建自己的类型,那么它可以是一个模型


关于您的最后一个问题,CUDA在运行时并不总是按照您描述的方式进行JIT。也许你所描述的最接近的东西可能是使用C++模板的东西,它是由CUDA支持的。一般来说,如果你可以将金属操作转换成等价的C++操作,你就可以直接实现CUDA中的那些。< /P>虽然你没有问过,但是如果是我,我不会把我的内核定义包在<代码>外“C”< /C>中。如果您想要C风格的可链接性,我建议您创建普通(即不使用
\uuuu device\uuuu
\uuu global\uuuu
修饰)包装函数,为您调用内核。在与内核定义相同的模块中定义这些包装器函数,并从需要C-linkability的其他模块中调用这些包装器函数。另外,我觉得您的循环很奇怪,因为循环体不依赖于循环变量
j
。但这不在这里也不在那里。我需要使用extern“C”,这样它就不会损坏名称。我在主机端使用驱动程序API,我希望能够在Cuda、Metal、Vulkan、OpenCL等之间以一致的方式引用内核函数。。。for循环用于在单个内核调用中执行一系列时间步,而不是每个时间步执行一个内核调用。在我最初的Metal和Cuda实现中进行的基准测试表明,与非循环情况相比,这产生了10倍的速度。Metal编译为中间表示,然后在加载内核函数时编译最终的机器代码。对于Cuda,我使用NVCC生成一个看起来像IR的PTX文件。然后使用驱动程序API
cuModuleLoad
加载它
cuModuleLoad
有一个
CUDA_错误\u JIT_编译器\u未找到
它可以返回,所以我认为它会在编译时生成最终的机器代码。我试图将Metal和CUDA内核代码都放在同一个源文件中,以避免代码重复。我想我可以在Cuda端定义缺少的操作符。是的,如果你有PTX,最终的机器代码可以在运行时生成。这对我来说并不明显,这就是你所期待的JIT。据我所知,在运行时生成“优化内核”与编译时生成“优化内核”没有什么特别的好处,除非您实际上是在运行时生成PTX,例如通过NVRTC生成。当您提到“内核的优化版本”时,我以为您在寻找类似于在运行时通过JIT实现numpy ufunc专门化的东西。作战命令
metal:  x[i] += dt*f(x[i]);

CUDA:   float2 temp1 = x[i];
        float2 temp2 = f(temp1);
        temp1.x += dt*temp2.x;
        temp1.y += dt*temp2.y;
        x[i] = temp1;