【问题标题】:Passing a function pointer and its parameters as a thrust::tuple to a global function将函数指针及其参数作为推力::元组传递给全局函数
【发布时间】:2016-10-24 16:42:41
【问题描述】:

我想做以下事情:

#include <thrust/tuple.h>
#include <tuple>

template<typename... Args>
void someFunction(void (*fp)(Args...), thrust::tuple<Args...> params) {
}

void otherFunction(int n) {
}

int main(int argc, char **argv) {
    //// template argument deduction/substitution failed ////
    someFunction<int>(&otherFunction, thrust::make_tuple(1));
    return 0;
}

我尝试过的:

  1. 删除这两个参数中的一个当然会得到一个可行的解决方案。
  2. 当我使用模板参数在struct 中将someFunction 设为静态函数时,它可以工作。但是在原始代码中someFunction 是一个CUDA 内核,所以我不能这样做。还有什么想法吗?
  3. 当我将thrust::tuple 更改为std::tuple 时它可以工作。有没有办法从 std::tuple 构造一个推力::tuple?

编辑:

为了更清楚:someFunctionotherFunction__global__

#include <thrust/tuple.h>
#include <tuple>

template<typename... Args>
__global__ void someFunction(void (*fp)(Args...), thrust::tuple<Args...> params) {
}

__global__ void otherFunction(int n) {
}
__constant__ void (*kfp)(int) = &otherFunction;

int testPassMain(int argc, char **argv) {
    void (*h_kfp)(int);
    cudaMemcpyFromSymbol(&h_kfp, kfp, sizeof(void *), 0, cudaMemcpyDeviceToHost);
    someFunction<int><<<1,1>>>(h_kfp, thrust::make_tuple(1));
    return 0;
}

我得到一个编译器错误:template argument deduction/substitution failed 在两个示例中。

【问题讨论】:

  • 也许 与您的问题无关,但您可能会从 all standard algorithm functions 那里得到一个提示,该提示将“谓词”作为参数。他们并不真正关心函数的参数,他们只为函数提供了一个 typename 模板参数。
  • 如果 someFunction 是一个 CUDA 内核(即一个 __global__ 函数),为什么你没有在你的例子中配置它(在启动时)或相应地装饰它?在我看来,这个问题很不清楚。 otherFunction 是否可以从 __global__ 函数调用?如果是这样,你为什么不相应地装饰它?您不能在主机代码中获取设备函数的地址,这似乎是您在这里所做的(即使您用__device__ 装饰了otherFunction,它仍然不会像写的那样工作)
  • 问题不在于从内核函数指针调用内核。我把这部分省略了,因为它有效。这是关于将带有可变参数模板的两个参数传递给全局函数时出现编译器错误。

标签: c++ c++11 cuda variadic-templates thrust


【解决方案1】:

将函数指针及其参数作为推力::元组传递给全局函数

这样的事情应该是可行的:

$ cat t1161.cu
#include <thrust/tuple.h>
#include <stdio.h>

template <typename T, typename T1>
__global__ void kernel(void (*fp)(T1), T params){ // "someFunction"

  fp(thrust::get<0>(params));
  fp(thrust::get<1>(params));
}

__device__ void df(int n){                        // "otherFunction"

  printf("parameter = %d\n", n);
}

__device__ void (*ddf)(int) = df;

int main(){

  void (*hdf)(int);
  thrust::tuple<int, int> my_tuple = thrust::make_tuple(1,2);
  cudaMemcpyFromSymbol(&hdf, ddf, sizeof(void *));
  kernel<<<1,1>>>(hdf, my_tuple);
  cudaDeviceSynchronize();
}


$ nvcc -o t1161 t1161.cu
$ cuda-memcheck ./t1161
========= CUDA-MEMCHECK
parameter = 1
parameter = 2
========= ERROR SUMMARY: 0 errors
$

如果您打算将df 用作__global__ 函数,则类似的方法也应该是可行的,您只需要正确考虑动态并行情况。同样,上面只有一个微小的变化应该允许您将元组直接传递给子函数(即df,无论是设备函数还是内核)。如果您的参数很好地打包在推力元组中,我不清楚为什么您需要可变参数模板参数。

编辑:如果您可以将元组传递给子内核(我不明白为什么您不能这样做,因为根据您更新的示例,元组和子内核共享相同的可变参数包),那么您仍然可以使用这种方法避免使用可变参数模板:

$ cat t1162.cu
#include <thrust/tuple.h>
#include <stdio.h>

template<typename T>
__global__ void someFunction(void (*fp)(T), T params) {
  fp<<<1,1>>>(params);
  cudaDeviceSynchronize();
}

__global__ void otherFunction(thrust::tuple<int> t) {
  printf("param 0 = %d\n", thrust::get<0>(t));
}

__global__ void otherFunction2(thrust::tuple<float, float> t) {
  printf("param 1 = %f\n", thrust::get<1>(t));
}
__device__ void (*kfp)(thrust::tuple<int>) = &otherFunction;
__device__ void (*kfp2)(thrust::tuple<float, float>) = &otherFunction2;

int main(int argc, char **argv) {
    void (*h_kfp)(thrust::tuple<int>);
    void (*h_kfp2)(thrust::tuple<float, float>);
    cudaMemcpyFromSymbol(&h_kfp, kfp, sizeof(void *), 0, cudaMemcpyDeviceToHost);
    someFunction<<<1,1>>>(h_kfp, thrust::make_tuple(1));
    cudaDeviceSynchronize();
    cudaMemcpyFromSymbol(&h_kfp2, kfp2, sizeof(void *), 0, cudaMemcpyDeviceToHost);
    someFunction<<<1,1>>>(h_kfp2, thrust::make_tuple(0.5f, 1.5f));
    cudaDeviceSynchronize();
    return 0;
}
$ nvcc -arch=sm_35 -rdc=true -o t1162 t1162.cu -lcudadevrt
$ CUDA_VISIBLE_DEVICES="1" cuda-memcheck ./t1162
========= CUDA-MEMCHECK
param 0 = 1
param 1 = 1.500000
========= ERROR SUMMARY: 0 errors
$

在功能方面(能够调度具有不同参数包的多个子内核)我看不出任何能力差异,再次假设您的参数很好地打包在一个元组中。

【讨论】:

  • 我使用可变参数模板的原因是,我想将任意内核函数指针传递给调度程序。元组包含一个内核的一组参数。
  • 我添加了第二种方法,我认为可以解决这个问题。您可以将任意内核函数指针传递给调度内核,然后调度内核使用提供的参数包调度这些内核。
  • 非常感谢您的帮助!我不想在调度程序之外更改内核(例如 otherFunction)。我解压缩元组以调用内核。我已经有一个工作正常的 CPU 调度程序,这是一个工作的 GPU 调度程序中唯一缺少的部分。还有什么想法吗?谢谢。
  • 我不认为与(*fp)(Args...) 共享来自thrust::tuple&lt;Args...&gt; 的参数包会起作用。在与您的函数关联的Args... 的情况下,它只是int,但在具有单个int 的推力元组的情况下,Args... 实际上是int, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type,所以我们得到以下替换失败消息后请注意:note: inconsistent parameter pack deduction...
  • 没错!推力::元组的大小固定为 10,这就是问题所在。正如我所说,它适用于静态函数,但不适用于全局函数。
【解决方案2】:

一个快速而肮脏的解决方案是强制转换函数指针:

#include <thrust/tuple.h>
#include <tuple>

template<typename... Args>
__global__ void someFunction(void (*fp)(), thrust::tuple<Args...> params) {
    void (*kfp)(Args...) = (void (*)(Args...)) fp;
    kfp<<<1,1>>>(thrust::get<0>(params));
}

__global__ void otherFunction(int n) {
    printf("n = %d\n", n);
}
__constant__ void (*kfp)(int) = &otherFunction;

int testPassMain(int argc, char **argv) {
    void (*h_kfp)();
    cudaMemcpyFromSymbol(&h_kfp, kfp, sizeof(void *), 0, cudaMemcpyDeviceToHost);
    someFunction<int><<<1,1>>>(h_kfp, thrust::make_tuple(1));
    return 0;
}

我愿意接受更好的解决方案!

【讨论】:

  • 我假设您希望能够使用任意参数集调度内核。这只能调度参数集已知的内核(例如,在您显示的示例中为int)。我看不出这如何符合您的问题描述,但是,无论如何。我关于将元组传递给子内核的第二个建议避免了这个限制,因此父内核不需要知道任何关于参数顺序的信息。
  • someFunction 可用于调度具有任意参数集的内核。 someFunction&lt;float, float&gt;&lt;&lt;&lt;1,1&gt;&gt;&gt;(h_kfp, thrust::make_tuple(1.0, 1.5)。 someFunction 可以使用参数解包来支持可变长度的参数,如下所示:stackoverflow.com/questions/7858817/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-17
  • 1970-01-01
  • 2012-11-23
  • 2021-07-18
  • 1970-01-01
  • 2013-01-25
  • 2016-11-07
相关资源
最近更新 更多