C++ 如何为CUDA中的多个推力对象成员函数调用内核函数?

C++ 如何为CUDA中的多个推力对象成员函数调用内核函数?,c++,cuda,nvidia,thrust,C++,Cuda,Nvidia,Thrust,请注意,我是CUDA的绝对初学者,下面的所有内容都是未经测试的伪代码。我来自JavaScript,我的C++也是非常生锈的,所以我为我的无知道歉: 我试图使用CUDA对许多不同的外汇策略进行回溯测试 使用推力,我从一个类(伪代码)实例化了1000个对象: 我如何使用CUDA来实现这一点,使.backtest()在每次迭代j通过数据并行运行所有策略 如果我必须彻底重建一切,那就随它去吧——我愿意接受任何必要的东西。如果这是不可能的类,那么就是它。 < P>典型的推力代码使用某些C++习语频繁(例如

请注意,我是CUDA的绝对初学者,下面的所有内容都是未经测试的伪代码。我来自JavaScript,我的C++也是非常生锈的,所以我为我的无知道歉: 我试图使用CUDA对许多不同的外汇策略进行回溯测试

使用推力,我从一个类(伪代码)实例化了1000个对象:

我如何使用CUDA来实现这一点,使
.backtest()
在每次迭代
j
通过数据并行运行所有策略


如果我必须彻底重建一切,那就随它去吧——我愿意接受任何必要的东西。如果这是不可能的类,那么就是它。

< P>典型的推力代码使用某些C++习语频繁(例如,函数),所以如果你的C++是生锈的,你可能想读关于C++函子,例如。您可能还想查看,以讨论函子以及我们目前将使用的奇特迭代器

总的来说,至少从表达式的角度来看,我认为推力非常适合您的问题描述。考虑到这些类型问题的推力表达式的灵活性,可能有很多方法可以解决问题。我将尝试展示一些与您的伪代码“相近”的东西。但毫无疑问,有很多方法可以实现这一点

首先,在推力方面,我们通常尽量避免循环。这将非常缓慢,因为它们通常在每次迭代时都涉及主机代码和设备代码交互(例如,在每次迭代时在引擎盖下调用CUDA内核)。如果可能的话,我们更喜欢使用推力算法,因为这些算法通常会在引擎盖下“转换”成一个或一小部分CUDA内核

推力中最基本的算法之一是推力。它有多种风格,但基本上需要输入数据并对其逐个元素应用任意操作

使用基本的推力转换操作,我们可以初始化数据和策略,而无需求助于for循环。我们将为每种类型的对象(
dataPoint
Strategy
)构造一个适当长度的设备向量,然后我们将使用
推力::变换
初始化每个向量

这就给我们留下了针对每个
策略执行每个
数据点
的任务。理想情况下,我们也希望并行地执行此操作;不仅对于您建议的每个for循环迭代,而且对于针对每个
数据点的每个
策略
,都是“一次”(即在单个算法调用中)

实际上,我们可以想象一个矩阵,一个轴由
数据点组成(在您的示例中为100000维),另一个轴由
策略组成(在您的示例中为1000维)。对于这个矩阵中的每个点,我们设想它持有应用
策略
数据点
的结果

在推力方面,我们通常更喜欢实现一维等二维概念。因此,我们的结果空间等于
数据点的数量乘以
策略的数量的乘积。我们将创建此大小的
result
device_向量(在您的示例中为100000*1000)来保存结果

为了演示,由于您对要执行的算术类型没有给出多少指导,我们将假设如下:

// Iterate over all 100000 data points.
for (j=0; j<100000; j++) {
    // Iterate over all 1000 strategies.
    for (i=0; i<1000; i++) {
        strategies[i].backtest(data[j]);
    }
}
  • 数据点应用
    策略
    的结果是
    浮动
  • dataPoint
    是一个由
    int
    dtype
    -在本例中被忽略)和
    float
    dval
    )组成的结构<对于
    数据点(i)
    1.0f+i*10.0f
    ,代码>dval
  • 将包含
  • 策略由一个
    乘法器
    和一个
    加法器
    组成,如下所示:

    Strategy(i) = multiplier(i) * dval + adder(i);
    
  • 数据点
    应用
    策略
    包括检索与
    数据点
    相关的
    dval
    ,并将其代入上文第3项给出的等式中。该等式在类
    策略
    回溯测试
    方法中捕获。
    backtest
    方法将类型为
    dataPoint
    的对象作为其参数,从中检索适当的
    dval

  • 我们还需要介绍一些概念。2D结果矩阵的1D实现需要我们提供适当的索引方法,以便在2D矩阵中的每个点,给定其线性尺寸,我们可以确定哪个
    策略
    ,哪个
    数据点
    将用于计算该点的
    结果
    。在Stress中,我们可以使用一组奇特的迭代器来实现这一点

    简而言之,从“由内而外”开始,我们将使用一个变换迭代器,该迭代器采用一个索引映射函子和一个由
    推力::计数迭代器提供的线性序列,以便为每个索引(每个矩阵维度)创建一个映射。每个映射函子中的算术运算将
    result
    的线性索引转换为矩阵行和列的适当重复索引。给定此转换迭代器来创建重复的行或列索引,我们将该索引传递给置换迭代器,置换迭代器为所指示的每一行/列选择适当的
    dataPoint
    Strategy
    。这两个项目(
    dataPoint
    Strategy
    )然后在
    zip\u迭代器中压缩在一起。然后将
    zip_迭代器
    传递给
    run_strat
    functor,该functor实际计算应用于给定
    数据点的给定
    策略

    下面是一个概述abo的示例代码
    Strategy(i) = multiplier(i) * dval + adder(i);
    
    #include <iostream>
    #include <thrust/device_vector.h>
    #include <thrust/host_vector.h>
    #include <thrust/transform.h>
    #include <thrust/iterator/counting_iterator.h>
    #include <thrust/iterator/permutation_iterator.h>
    #include <thrust/iterator/zip_iterator.h>
    #include <math.h>
    
    #define TOL 0.00001f
    
    
    // number of strategies
    #define N 1000
    // number of data points
    #define DSIZE 100000
    
    // could use int instead of size_t here, for these problem dimensions
    typedef size_t idx_t;
    
    
    struct dataPoint {
    
      int dtype;
      float dval;
    };
    
    class Strategy {
    
        float multiplier;
        float adder;
        idx_t id;
    
        public:
    
            __device__ __host__ Strategy(){
              id = 0;
              multiplier = 0.0f;
              adder = 0.0f;
              }
            __device__ __host__ Strategy(idx_t _id) {
              id = _id;
              multiplier = 1.0f + ((float)id)/(float)N;
              adder = (float)id;
            }
    
            __device__ __host__ float backtest(dataPoint data) {
              return multiplier*data.dval+adder;
            }
    };
    
    // functor to initialize dataPoint
    struct data_init
    {
      __host__ __device__
      dataPoint operator()(idx_t id){
        dataPoint temp;
        temp.dtype = id;
        temp.dval = 1.0f + id * 10.0f;
        return temp;
        }
    };
    
    // functor to initialize Strategy
    struct strat_init
    {
      __host__ __device__
      Strategy operator()(idx_t id){
        Strategy temp(id);
        return temp;
        }
    };
    
    // functor to "test" a Strategy against a dataPoint, using backtest method
    struct run_strat
    {
      template <typename T>
      __host__ __device__
      float operator()(idx_t id, T t){
        return (thrust::get<0>(t)).backtest(thrust::get<1>(t));
        }
    };
    
    // mapping functor to generate "row" (Strategy) index from linear index
    struct strat_mapper : public thrust::unary_function<idx_t, idx_t>
    {
      __host__ __device__
      idx_t operator()(idx_t id){
        return id/DSIZE;
        }
    };
    
    // mapping functor to generate "column" (dataPoint) index from linear index
    struct data_mapper : public thrust::unary_function<idx_t, idx_t>
    {
      __host__ __device__
      idx_t operator()(idx_t id){
        return id%DSIZE;
        }
    };
    
    
    
    int main() {
        // initialize data
        thrust::device_vector<dataPoint> data(DSIZE);
        thrust::transform(thrust::counting_iterator<idx_t>(0), thrust::counting_iterator<idx_t>(DSIZE), data.begin(), data_init());
    
        // initialize strategies
        thrust::device_vector<Strategy> strategies(N);
        thrust::transform(thrust::counting_iterator<idx_t>(0), thrust::counting_iterator<idx_t>(N), strategies.begin(), strat_init());
    
        // test each data point against each strategy
    
            // Somehow run .backtest(data[j]) on each strategy here.
            // i.e. Run backtest() in parallel for all 1000
            // strategy objects here.
    
        // allocate space for results for each datapoint against each strategy
        thrust::device_vector<float> result(DSIZE*N);
        thrust::transform(thrust::counting_iterator<idx_t>(0), thrust::counting_iterator<idx_t>(DSIZE*N), thrust::make_zip_iterator(thrust::make_tuple(thrust::make_permutation_iterator(strategies.begin(), thrust::make_transform_iterator(thrust::counting_iterator<idx_t>(0), strat_mapper())), thrust::make_permutation_iterator(data.begin(), thrust::make_transform_iterator(thrust::counting_iterator<idx_t>(0), data_mapper())))), result.begin(), run_strat());
    
        // validation
        // this would have to be changed if you change initialization of dataPoint
        // or Strategy
        thrust::host_vector<float> h_result = result;
        for (int j = 0; j < N; j++){
          float m =  1.0f + (float)j/(float)N;
          float a = j;
          for (int i = 0; i < DSIZE; i++){
            float d =  1.0f + i*10.0f;
            if (fabsf(h_result[j*DSIZE+i] - (m*d+a))/(m*d+a) > TOL) {std::cout << "mismatch at: " << i << "," << j << " was: " << h_result[j*DSIZE+i] << " should be: " << m*d+a << std::endl; return 1;}}}
        return 0;
    }