用CUDA计算相应矩阵行之间的欧氏距离

用CUDA计算相应矩阵行之间的欧氏距离,cuda,parallel-processing,gpu,Cuda,Parallel Processing,Gpu,我有一个非常简单的算法,计算两个矩阵对应行之间的平方欧几里德距离。我有以下代码,但不幸的是,对于不同的矩阵大小,它没有返回正确的结果。更具体地说,它适用于大小为2000x4,500x4,2500x2,600x8,1000x8,100x8的矩阵,但不适用于大小为2500x3,2500x5,400x3,100x3,100x10,的矩阵,1000x12,500x12,500x14 有人能帮我吗?我想手动执行,不使用任何优化的库,因为我想了解线程管理 __global__ void cudaEuclid

我有一个非常简单的算法,计算两个矩阵对应行之间的平方欧几里德距离。我有以下代码,但不幸的是,对于不同的矩阵大小,它没有返回正确的结果。更具体地说,它适用于大小为
2000x4
500x4
2500x2
600x8
1000x8
100x8
的矩阵,但不适用于大小为
2500x3
2500x5
400x3
100x3
100x10
的矩阵,
1000x12
500x12
500x14

有人能帮我吗?我想手动执行,不使用任何优化的库,因为我想了解线程管理

__global__ void cudaEuclid( float* A, float* B, float* C, int rows, int cols )
    {
        int i, squareeucldist = 0;
        int r = blockDim.x * blockIdx.x + threadIdx.x; // rows
        int c = blockDim.y * blockIdx.y + threadIdx.y; // cols 
        extern __shared__ float sdata[];
        //int r = blockIdx.y; int c = threadIdx.x;
        if( r < rows && c < cols  ){

            //C[r + rows*c] = ( A[r + rows*c] - B[r + rows*c] ) * ( A[r + rows*c] - B[r + rows*c] );


            sdata[threadIdx.x] = ( A[r + rows*c] - B[r + rows*c] ) * ( A[r + rows*c] - B[r + rows*c] );

            __syncthreads();

            // contiguous range pattern
            for(int offset = blockDim.x / 2;
                offset > 0;
                offset >>= 1)
            {
                if(threadIdx.x < offset)
                {
                    // add a partial sum upstream to our own
                    sdata[threadIdx.x] += sdata[threadIdx.x + offset];
                }

                // wait until all threads in the block have
                // updated their partial sums
                __syncthreads();
            }

            // thread 0 writes the final result
            if(threadIdx.x == 0)
            {
                C[r] = sdata[0];
            }

        }

    }
\uuuuu全局\uuuuu无效cudaEuclid(浮点*A、浮点*B、浮点*C、整数行、整数列)
{
int i,squareUCLDist=0;
int r=blockDim.x*blockIdx.x+threadIdx.x;//行
int c=blockDim.y*blockIdx.y+threadIdx.y;//cols
外部共享浮点数据[];
//int r=blockIdx.y;int c=threadIdx.x;
if(r0;
偏移量>>=1)
{
if(螺纹IDX.x<偏移量)
{
//在我们自己的上游加上一部分金额
sdata[threadIdx.x]+=sdata[threadIdx.x+偏移量];
}
//等待,直到块中的所有线程都已完成
//更新了他们的部分总和
__同步线程();
}
//线程0写入最终结果
if(threadIdx.x==0)
{
C[r]=sdata[0];
}
}
}
内核调用是:

dim3 dimBlock( cols, 1 ); 
dim3 dimGrid( 1, rows ); 
cudaEuclid<<<dimGrid, cols, cols*sizeof(float)>>>( d_A, d_B, d_C, rows, cols );
dim3 dimBlock(cols,1);
dim3 dimGrid(1行);
cudaEuclid(d_A、d_B、d_C、rows、cols);

PS:我想说的是,我发布了一个类似的问题,但从一开始就不清楚,讨论方向混乱。尽管Tom提出了一个非常有用的建议,即它在未来的优化实现中将非常实用,但我需要一些更手工的东西。最后,我写这篇文章的原因是因为我不想让相关的文章变得更复杂。谢谢。

事实上,只有当
n
足够小时,您的代码才能在
m*2^n
上工作。您可能想更仔细地阅读第14页的以下幻灯片

思考以下问题

  • blockDim.x
    等于3或5时会发生什么
  • blockDim.x
    cols
    不是2的幂时,如何正确执行并行缩减
  • 为什么减少的结果小于预期
  • sdata[]
    中的哪些元素未添加到最终总和中
  • cols
    为5时,如果将
    blockDim.x
    和smem的大小设置为2^3,结果是否正确
  • 对于q5,如何处理
    smem[5..7]

  • 试着用你的笔和纸一步一步地模拟运行for循环会有所帮助。

    尽管OP不想使用优化的库来回答他的问题,但这篇文章有一个有用的标题,其他用户可以发现,在没有手写内核的情况下解决这个问题很有用

    我很好奇,并对这个问题进行了一些研究,考虑使用CUDA推力。我以下面的代码结束,该代码通过使用
    推力::reduce_by_key
    计算两个矩阵的同源行之间的距离

    #include <thrust\device_vector.h>
    #include <thrust\transform_reduce.h>
    #include <thrust\sequence.h>
    #include <thrust\random.h>
    #include <thrust\gather.h>
    #include <thrust\extrema.h>
    
    using namespace thrust::placeholders;
    
    /****************************************************/
    /* POWER DIFFERENCE FUNCTOR FOR EUCLIDEAN DISTANCES */
    /****************************************************/
    struct PowerDifference {
        __host__ __device__ float operator()(const float& a, const float& b) const { return pow(a - b, 2); }
    };
    
    /*******************/
    /* EXPAND OPERATOR */
    /*******************/
    template <typename InputIterator1, typename InputIterator2, typename OutputIterator>
    OutputIterator expand(InputIterator1 first1,
                          InputIterator1 last1,
                          InputIterator2 first2,
                          OutputIterator output)
    {
        typedef typename thrust::iterator_difference<InputIterator1>::type difference_type;
    
        difference_type input_size  = thrust::distance(first1, last1);
        difference_type output_size = thrust::reduce(first1, last1);
    
        // scan the counts to obtain output offsets for each input element
        thrust::device_vector<difference_type> output_offsets(input_size, 0);
        thrust::exclusive_scan(first1, last1, output_offsets.begin()); 
    
        // scatter the nonzero counts into their corresponding output positions
        thrust::device_vector<difference_type> output_indices(output_size, 0);
        thrust::scatter_if(thrust::counting_iterator<difference_type>(0), thrust::counting_iterator<difference_type>(input_size),
                           output_offsets.begin(), first1, output_indices.begin());
    
        // compute max-scan over the output indices, filling in the holes
        thrust::inclusive_scan(output_indices.begin(), output_indices.end(), output_indices.begin(), thrust::maximum<difference_type>());
    
        // gather input values according to index array (output = first2[output_indices])
        OutputIterator output_end = output; thrust::advance(output_end, output_size);
        thrust::gather(output_indices.begin(), output_indices.end(), first2, output);
    
        // return output + output_size
        thrust::advance(output, output_size);
    
        return output;
    }
    
    /********/
    /* MAIN */
    /********/
    int main()
    {
        /**************************/
        /* SETTING UP THE PROBLEM */
        /**************************/
    
        const int N     = 10;           // --- Number of vector elements
        const int Nvec  = 20;           // --- Number of vectors for each matrix
    
        // --- Random uniform integer distribution between 0 and 100
        thrust::default_random_engine rng;
        thrust::uniform_int_distribution<int> dist(0, 20);
    
        // --- Matrix allocation and initialization
        thrust::device_vector<float> d_matrix1(Nvec * N);
        thrust::device_vector<float> d_matrix2(Nvec * N);
        for (size_t i = 0; i < d_matrix1.size(); i++) d_matrix1[i] = (float)dist(rng);
        for (size_t i = 0; i < d_matrix2.size(); i++) d_matrix2[i] = (float)dist(rng);
    
        printf("\n\nFirst matrix\n");
        for(int i = 0; i < Nvec; i++) {
            std::cout << " [ ";
            for(int j = 0; j < N; j++)
                std::cout << d_matrix1[i * N + j] << " ";
            std::cout << "]\n";
        }
    
        printf("\n\nSecond matrix\n");
        for(int i = 0; i < Nvec; i++) {
            std::cout << " [ ";
            for(int j = 0; j < N; j++)
                std::cout << d_matrix2[i * N + j] << " ";
            std::cout << "]\n";
        }
    
        /****************************************************************************/
        /* CALCULATING THE EUCLIDEAN DISTANCES BETWEEN THE ROWS OF THE TWO MATRICES */
        /****************************************************************************/
        // --- Creating the indices for the reduction by key
        thrust::device_vector<int> d_sequence(Nvec);
        thrust::device_vector<int> d_indices(Nvec * N);
        thrust::device_vector<int> d_counts(Nvec, N);
        thrust::sequence(d_sequence.begin(), d_sequence.begin() + Nvec);
        expand(d_counts.begin(), d_counts.end(), d_sequence.begin(), d_indices.begin());
    
        printf("\n\nSecond matrix\n");
        for(int i = 0; i < Nvec; i++) {
            std::cout << " [ ";
            for(int j = 0; j < N; j++)
                std::cout << d_indices[i * N + j] << " ";
            std::cout << "]\n";
        }
    
        thrust::device_vector<float> d_squared_differences(Nvec * N);
    
        thrust::transform(d_matrix1.begin(), d_matrix1.end(), d_matrix2.begin(), d_squared_differences.begin(), PowerDifference());
    
        thrust::device_vector<float> d_norms(Nvec);
        thrust::reduce_by_key(d_indices.begin(), d_indices.end(), d_squared_differences.begin(), d_indices.begin(), d_norms.begin());
    
        printf("\n\ndnorms\n");
        for(int i = 0; i < Nvec; i++) {
                std::cout << d_norms[i] << " ";
        }
    
        return 0; 
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    使用命名空间推力::占位符;
    /****************************************************/
    /*欧氏距离的幂差函子*/
    /****************************************************/
    结构功率差{
    __主机___设备_;浮点运算符()(常量浮点&a,常量浮点&b)常量{return pow(a-b,2);}
    };
    /*******************/
    /*扩展运算符*/
    /*******************/
    模板
    输出迭代器扩展(输入迭代器1优先1,
    输入计数器1 last1,
    输入计数器2 first2,
    输出计数器(输出)
    {
    typedef typename推力::迭代器_差异::类型差异_类型;
    差异类型输入大小=推力::距离(第一个1,最后一个1);
    差异类型输出大小=推力::减小(第一个1,最后一个1);
    //扫描计数以获得每个输入元素的输出偏移量
    推力::设备矢量输出偏移量(输入大小,0);
    推力:独占扫描(first1,last1,output_offset.begin());
    //将非零计数分散到相应的输出位置
    推力:设备矢量输出指数(输出大小,0);
    推力::分散if(推力::计数迭代器(0),推力::计数迭代器(输入大小),
    output_offset.begin(),first1,output_index.begin());
    //计算输出索引的最大扫描,填充孔
    推力::包括性推力扫描(输出推力索引.begin(),输出推力索引.end(),输出推力索引.begin(),推力::最大值());
    //根据索引数组收集输入值(output=first2[output\u index])
    输出计数器输出端=输出;推力::前进(输出端,输出大小);
    推力::聚集(输出索引.begin(),输出索引.end(),first2,输出);
    //返回输出+输出大小
    推力:前进(输出、输出尺寸);
    返回输出;
    }
    /********/
    /*主要*/
    /********/
    我