0

I need a function that computes some "math" function of several variables on GPU. I decided to use Thrust and its zip_iterator to pack variables in a tuple, and implement my math function as a functor foк for_each. But I'd like to have a universal function that can compute different "math" functions. So, I need to pass this functor in the function.

As I think, to do this task, I should implement some simple hierarchy (with the only base class) of functors with different versions of operator()(Tuple t). For example, functors can be like these:

struct Func {
    template <typename Tuple>
    __host__ __device__
    void operator()(Tuple arg) {
        thrust::get<1>(arg) = 0;
    }
};

strust f1 : public Func {
   ...
};

strust f2 : public Func {
   ...
};

The question is how can I properly pass required functor into the function, and how to define such a function? This function can be like:

thrust::host_vector evaluateFunction(Func func, thrust::host_vector point) {
    thrust::host_vector result(point.size());

    thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(point.begin(), result.begin())),
    thrust::make_zip_iterator(thrust::make_tuple(point.end(), result.end())),
                 func);
    return result;
}

but with such a definition I cannot pass f1 or f2 into it. How can I define it properly?

lexxa2000
  • 9
  • 5
  • 1
    How will you identify the particular "math" function that you want to use, in a particular scenario? I would say you need to pass (or identify) the *function* to the *functor*, not the other way around. – Robert Crovella Apr 30 '14 at 18:27
  • I thought that I could implement required "math" functions as functors, and also implement a function, that computes this `math` function for every element of a given vector. So this function is universal, but `math` function (implemented as a functor) should be passed into it. – lexxa2000 May 01 '14 at 05:21
  • You will (must) pass a functor to `thrust::for_each`. If you intend to pass individual (separate) functors for each math function, then I don't see what the difficulty is, assuming you want to apply the same math function to each element. If you want to pass a different functor for each element, you can't do that. But if you want to pass the same functor for each element, but have (possibly) a different math computation for each element, that is possible, but you will have to have some method, element-by-element, of identifying what you want to compute. I don't think your question is clear. – Robert Crovella May 01 '14 at 13:03
  • Thanks @Robert. I have edited my post and insert some code. I intend to pass individual functor for each element. – lexxa2000 May 04 '14 at 04:57
  • What you've now shown doesn't look like [polymorphism](http://www.cplusplus.com/doc/tutorial/polymorphism/) to me. You simply want to be able to pass a function pointer to a functor. Furthermore, what you've shown doesn't look like you intend to pass an individual functor (or function pointer) for each *element* of a vector. It appears you want to apply the same function (e.g. `func`) to *every* element of a vector. – Robert Crovella May 04 '14 at 19:30

1 Answers1

1

It appears that you want to pass a user-defined or user-selectable function to thrust, for evaluation in a functor. Based on that, I think a possible answer is very similar to what is posted here, however I'll make some other comments.

  1. I don't think you were actually asking about this, but I think polymorphism expressed in host classes, objects of which that are eventually passed to the device for use in a thrust::device_vector would be essentially impossible, because It is not allowed to pass as an argument to a __global__ function an object of a class with virtual functions. Although it's not entirely obvious, this is in effect what I would be doing if I created an appropriate array of objects on the host and then attempted to use them polymorphically in a thrust::device_vector. At least, I could not fight my way through the jungle. You can use polymorphism in device code, of course, assuming the objects are correctly created on the device so that their virtual function tables can be properly assigned. This question and comments may also be of interest. If you want to see an example of using polymorphism on device objects, take a look at my answer here. It demonstrates the idea of using the object itself to define the function you wish to perform on it. (Although, again, I don't think that is what you had in mind.)
  2. Based on your edited question, it appears that you'd like to pass the address of a function, for use in a functor. This is also (at least in a straightforward manner) impossible with CUDA, because it is not allowed to take the address of a __device__ function in host code. This limitation can be worked around, after a fashion, by calling an "initialization kernel" which fills in a table of device function pointers for use in device code, but I think the net effect is no better than what I propose in 3 below.
  3. You can pass a user-selectable index of a function, for use in device code.

Here's an example which demonstrates this last idea:

#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <thrust/copy.h>
#include <thrust/for_each.h>
#include <thrust/iterator/zip_iterator.h>
#include <iostream>
#include <math.h>


__host__ __device__ float f1(float x)
{
  return sinf(x);
}

__host__ __device__ float f2(float x)
{
  return cosf(x);
}



struct select_functor
{
  unsigned fn;

  select_functor(unsigned _fn) : fn(_fn) {};
  template <typename Tuple>
  __host__ __device__
  void operator()(const Tuple &t) {
    if (fn == 1) thrust::get<1>(t)  = f1(thrust::get<0>(t));
    else if (fn == 2) thrust::get<1>(t)  = f2(thrust::get<0>(t));
    else thrust::get<1>(t) = 0;
  }
};


int main(void)
{
  unsigned ufn = 1;
  const unsigned N = 8;
  thrust::device_vector<float> data(N), result(N);
  // initilaize to some values
  thrust::sequence(data.begin(), data.end(),  0.0f, (float)(6.283/(float)N));
  std::cout<< "x: " << std::endl;
  thrust::copy(data.begin(), data.end(), std::ostream_iterator<float>(std::cout, " "));
  thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(data.begin(), result.begin())),thrust::make_zip_iterator(thrust::make_tuple(data.end(),result.end())),select_functor(ufn));
  std::cout<< std::endl << "sin(x): " << std::endl;
  thrust::copy(result.begin(), result.end(), std::ostream_iterator<float>(std::cout, " "));
  ufn = 2;
  thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(data.begin(), result.begin())),thrust::make_zip_iterator(thrust::make_tuple(data.end(),result.end())),select_functor(ufn));
  std::cout<< std::endl << "cos(x): " << std::endl;
  thrust::copy(result.begin(), result.end(), std::ostream_iterator<float>(std::cout, " "));
  std::cout<< std::endl;
  return 0;
}
Community
  • 1
  • 1
Robert Crovella
  • 143,785
  • 11
  • 213
  • 257