使用tag-dispatch

时间:2015-07-20 11:14:48

标签: c++ memory-management stl cuda

创建了一个STL风格的分配器识别类,我尝试将其与自定义CUDA分配器一起使用。 CUDA分配器适用于在统一内存中分配数据存储,但为了使this指针可在主机和设备上访问,我需要确保每当数据在统一内存中分配时,班级也是如此。

为了解决这个问题,我认为简单的标签发送是合适的。如果分配器是cudaAllocator,则new应该在统一内存中创建类,如果不是,则应该返回常规新输出。不幸的是,我认为我错过了tag-dispatch的工作方式。这是班级的相关部分:

#ifdef __CUDACC__
public:

    using cudaAllocator_tag = std::true_type;
    using hostAllocator_tag = std::false_type;

    void *operator new(size_t len)
    {
        return new(len, std::is_same<Alloc, cudaAllocator<value_type>>());
    }

    void operator delete(void *ptr) 
    {
        return delete(ptr, std::is_same<Alloc, cudaAllocator<value_type>>());
    }

    void *operator new(size_t len, cudaAllocator_tag)
    {
        void *ptr;
        cudaMallocManaged(&ptr, len);
        cudaDeviceSynchronize();
        return ptr;
    }

    void operator delete(void *ptr, cudaAllocator_tag) 
    {
        cudaDeviceSynchronize();
        cudaFree(ptr);
    }

    void *operator new(size_t len, hostAllocator_tag)
    {
        return ::new(len);
    }

    void operator delete(void *ptr, hostAllocator_tag)
    {
        ::delete(ptr);
    }

#endif // __CUDACC__

但是(NVCC)编译器会抛出以下错误:

2> error : expected a type specifier
2>  
2>            detected during instantiation of "void *CircularQueue<T, Alloc>::operator new(size_t) [with T=float, Alloc=cudaAllocator<float>]" 
2>  
2>  main.cu(21): here
2>  
2>  
2>  
2> error : no instance of overloaded "operator new" matches the argument list
2>  
2>              argument types are: (unsigned long long, size_t, std::is_same<cudaAllocator<float>, cudaAllocator<float>>)
2>  
2>            detected during instantiation of "void *CircularQueue<T, Alloc>::operator new(size_t) [with T=float, Alloc=cudaAllocator<float>]" 
2>  
2>  main.cu(21): here

关于我做错的任何想法?

1 个答案:

答案 0 :(得分:1)

这里有几个问题:

  1. new缺少要创建的对象的标识符。假设您的容器被调用myContainer它应该是:

    static void *operator new(size_t len)
    {
        return new myContainer(len, std::is_same<Alloc, cudaAllocator<value_type>>());
    }
    
  2. operator delete的参数不能像新的一样重载。您可以通过delete调用自定义destroy函数来解决此问题,使用内联和标记调度来避免任何运行时处罚。

    static void operator delete(void *ptr) 
    {
        destroy(ptr, std::is_same<Alloc, cudaAllocator<value_type>>());
    }
    

    为了避免混淆/无限递归,最好也可以使用new

  3. 完整解决方案:

    #ifdef __CUDACC__
    public:
    
        using cudaAllocator_tag = std::true_type;
        using hostAllocator_tag = std::false_type;
        using isCudaAllocator   = typename std::is_same<Alloc, cudaAllocator<value_type>>;
    
        static void *operator new(size_t len)
        {
            return create(len, isCudaAllocator());
        }
    
        static void operator delete(void *ptr) 
        {
            destroy(ptr, isCudaAllocator());
        }
    
    protected:
    
        static inline void *create(size_t len, cudaAllocator_tag)
        {
            void *ptr;
            cudaMallocManaged(&ptr, len);
            cudaDeviceSynchronize();
            return ptr;
        }
    
        static inline void destroy(void *ptr, cudaAllocator_tag)
        {
            cudaDeviceSynchronize();
            cudaFree(ptr);
        }
    
        static inline void *create(size_t len, hostAllocator_tag)
        {
            return ::new CircularQueue(len);
        }
    
        static inline void destroy(void *ptr, hostAllocator_tag)
        {
            ::delete(static_cast<CircularQueue*>(ptr));
        }
    #endif // __CUDACC__