Search code examples
c++cudathrust

How to thrust::make_transform_iterator that dereferences a device_ptr?


New to C++ and CUDA. Using MSVS 2015 Community and CUDA 9.2.

I tried making a transform_iterator that just dereferences a device_ptr.

I get a compilation error: function "dereference_device_double_functor::operator()" cannot be called with the given argument list

I also made a version that uses a host_vector and just a normal double pointer to make sure my functor usage is right.

    #include <iostream>
    #include "thrust\device_vector.h"
    #include "thrust\host_vector.h"

    struct dereference_device_double_functor
    {
        dereference_device_double_functor() {}

        typedef thrust::device_reference<thrust::device_ptr<double>> argument_type;
        typedef double result_type;

        __host__ __device__
            double operator()(thrust::device_reference<thrust::device_ptr<double>> xDpRef) const {
            thrust::device_ptr<double> xDp = (thrust::device_ptr<double>)xDpRef;
            return *xDp;
        }
    };

    struct dereference_host_double_functor
    {
        dereference_host_double_functor() {}

        typedef double* argument_type;
        typedef double result_type;

        __host__ __device__
            double operator()(double* const& xPtr) const {
            return *xPtr;
        }
    };

    int main()
    {
        // Create double
        thrust::device_vector<double> dv(1, 5);
        thrust::host_vector<double> hv(1, 6);

        // Make sure its there
        std::cout << dv[0] << std::endl;
        std::cout << hv[0] << std::endl;

        // Create pointers to doubles
        thrust::device_vector<thrust::device_ptr<double>> dvPtr(1);
        thrust::device_vector<double*> hvPtr(1);

        // Assign pointers to doubles
        dvPtr[0] = &(dv[0]);
        hvPtr[0] = &(hv[0]);

        // Make sure pointers point correctly
        std::cout << *((thrust::device_ptr<double>)dvPtr[0]) << std::endl;
        std::cout << *(hvPtr[0]) << std::endl;

        // Test functor with iterator
        auto dvi = dvPtr.begin();
        double dvd = dereference_device_double_functor()(*dvi);
        auto hvi = hvPtr.begin();
        double hvd = dereference_host_double_functor()(*hvi);

        // Make sure it worked with iterator
        std::cout << dvd << std::endl;
        std::cout << hvd << std::endl;

        // Make dereferencing transfom iterators
        auto tik = thrust::make_transform_iterator(dvPtr.begin(), dereference_device_double_functor());
        auto tij = thrust::make_transform_iterator(hvPtr.begin(), dereference_host_double_functor());

        // Check that transform iterators work
        //std::cout << *tik << std::endl; // Will cause compile error: function "dereference_device_double_functor::operator()" cannot be called with the given argument list
        std::cout << *tij << std::endl;

        return 0;
    }

Thanks for all your help!


Solution

  • In your question, you state this:

    I tried making a transform_iterator that just dereferences a device_ptr.

    That's not what I see in your code, however:

        __host__ __device__
            double operator()(thrust::device_reference<thrust::device_ptr<double>> xDpRef) const {
    

    When I compile your code on linux, I get the following (excerpted from compile error spew):

    $ nvcc -std=c++11 -o t351 t351.cu
    /usr/local/cuda/bin/..//include/thrust/iterator/transform_iterator.h(312): error: function "dereference_device_double_functor::operator()" cannot be called with the given argument list
                argument types are: (thrust::device_ptr<double>)
                object type is: dereference_device_double_functor
    ...
    

    So thrust is passing you a thrust::device_ptr<double>. But your functor operator is configured to take a thrust::device_reference<thrust::device_ptr<double>>

    When I modify your code from this:

        __host__ __device__
            double operator()(thrust::device_reference<thrust::device_ptr<double>> xDpRef) const {
    

    to this:

        __host__ __device__
            double operator()(thrust::device_ptr<double> xDpRef) const {
    

    It compiles and runs correctly for me (on linux):

    $ cat t351.cu
     #include <iostream>
        #include <thrust/device_vector.h>
        #include <thrust/host_vector.h>
    
        struct dereference_device_double_functor
        {
            dereference_device_double_functor() {}
    
            typedef thrust::device_reference<thrust::device_ptr<double>> argument_type;
            typedef double result_type;
    
            __host__ __device__
                double operator()(thrust::device_ptr<double> xDpRef) const {
                thrust::device_ptr<double> xDp = (thrust::device_ptr<double>)xDpRef;
                return *xDp;
            }
        };
    
        struct dereference_host_double_functor
        {
            dereference_host_double_functor() {}
    
            typedef double* argument_type;
            typedef double result_type;
    
            __host__ __device__
                double operator()(double* const& xPtr) const {
                return *xPtr;
            }
        };
    
        int main()
        {
            // Create double
            thrust::device_vector<double> dv(1, 5);
            thrust::host_vector<double> hv(1, 6);
    
            // Make sure its there
            std::cout << dv[0] << std::endl;
            std::cout << hv[0] << std::endl;
    
            // Create pointers to doubles
            thrust::device_vector<thrust::device_ptr<double>> dvPtr(1);
            thrust::device_vector<double*> hvPtr(1);
    
            // Assign pointers to doubles
            dvPtr[0] = &(dv[0]);
            hvPtr[0] = &(hv[0]);
    
            // Make sure pointers point correctly
            std::cout << *((thrust::device_ptr<double>)dvPtr[0]) << std::endl;
            std::cout << *(hvPtr[0]) << std::endl;
    
            // Test functor with iterator
            auto dvi = dvPtr.begin();
            double dvd = dereference_device_double_functor()(*dvi);
            auto hvi = hvPtr.begin();
            double hvd = dereference_host_double_functor()(*hvi);
    
            // Make sure it worked with iterator
            std::cout << dvd << std::endl;
            std::cout << hvd << std::endl;
    
            // Make dereferencing transfom iterators
            auto tik = thrust::make_transform_iterator(dvPtr.begin(), dereference_device_double_functor());
            auto tij = thrust::make_transform_iterator(hvPtr.begin(), dereference_host_double_functor());
    
            // Check that transform iterators work
            std::cout << *tik << std::endl; // Will cause compile error: function "dereference_device_double_functor::operator()" cannot be called with the given argument list
            std::cout << *tij << std::endl;
    
            return 0;
        }
    $ nvcc -std=c++11 -o t351 t351.cu
    $ cuda-memcheck ./t351
    ========= CUDA-MEMCHECK
    5
    6
    5
    6
    5
    6
    5
    6
    ========= ERROR SUMMARY: 0 errors
    $