CUDA通过缩减计算中值

CUDA通过缩减计算中值,cuda,median-of-medians,Cuda,Median Of Medians,我可能在做一些非常愚蠢的事情,但我似乎无法让这个减少工作(可能有一个图书馆已经这样做了,但这是为了自学,所以请容忍我)。我试图通过采用中间值方法来找到整数项数组的中间值,我在下面进行了编码: __global__ void gpuMedOdd(int *entries, int *med) { extern __shared__ int sdata[]; int tid = threadIdx.x; int i = blockIdx.x * bl

我可能在做一些非常愚蠢的事情,但我似乎无法让这个减少工作(可能有一个图书馆已经这样做了,但这是为了自学,所以请容忍我)。我试图通过采用中间值方法来找到整数项数组的中间值,我在下面进行了编码:

__global__ void gpuMedOdd(int *entries, int *med) {
        extern __shared__ int sdata[];

        int tid = threadIdx.x;
        int i = blockIdx.x * blockDim.x + threadIdx.x;

        sdata[tid] = entries[i];
        __syncthreads();

        for(int s = blockDim.x / 3; s > 0; s /= 3) {
                if(tid < s) {
                        int list[3];
                        list[0] = sdata[tid], list[1] = sdata[tid + s], list[2] = sdata[tid + 2 * s];
                        if(list[1] < list[0])
                                swapGpu(list[1], list[0]);
                        if(list[2] < list[0])
                                swapGpu(list[2], list[0]);
                        if(list[2] < list[1])
                                swapGpu(list[2], list[1]);

                        sdata[tid] = list[1];
                }

                __syncthreads();
        }

        *med = sdata[0];
}
Edit2:如下所示,这是代码的全部内容

#include <iostream>
#include <fstream>
#include <cstdlib>

#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)
#define getLastCudaError(msg) __getLastCudaError(msg, __FILE__, __LINE__)

inline void __checkCudaErrors(cudaError err, const char *file, const int line) {
        if(cudaSuccess != err) {
                std::cout << file << "(" << line << ") : CUDA Runtime API error " << (int) err << ": " << cudaGetErrorString(err) << std::endl;
                exit(3);
        }
}

inline void __getLastCudaError(const char *errorMsg, const char *file, const int line) {
        cudaError_t err = cudaGetLastError();
        if(cudaSuccess != err) {
                std::cout << file << "(" << line << ") : getLastCudaError() CUDA error : " << errorMsg << " : (" << (int) err << ") " << cudaGetErrorString(err) << std::endl;
                exit(3);
        }
}

int cpuMin(int *entries, int numEntries) {
        int minVal = entries[0];

        for(int i = 1; i < numEntries; i++)
                if(entries[i] < minVal)
                        minVal = entries[i];

        return minVal;
}

int cpuMax(int *entries, int numEntries) {
        int maxVal = entries[0];

        for(int i = 1; i < numEntries; i++)
                if(entries[i] > maxVal)
                        maxVal = entries[i];

        return maxVal;
}

inline void swap(int a, int b) {
        int dum = a;
        a = b;
        b = dum;
}

__device__ inline void swapGpu(int a, int b) {
        int dum = a;
        a = b;
        b = dum;
}

__global__ void gpuMedOdd(int *entries, int *med, int numEntries) {
        extern __shared__ int sdata[];

        int tid = threadIdx.x;
        int i = blockIdx.x * (blockDim.x * 3) + threadIdx.x;

        if(i + 2 * blockDim.x < numEntries) {
                int list[3];
                list[0] = entries[i], list[1] = entries[i + blockDim.x], list[2] = entries[i + 2 * blockDim.x];
                if(list[1] < list[0])
                        swapGpu(list[1], list[0]);
                if(list[2] < list[0])
                        swapGpu(list[2], list[0]);
                if(list[2] < list[1])
                        swapGpu(list[2], list[1]);

                sdata[tid] = list[1];
        }

        __syncthreads();

        for(int s = blockDim.x / 3; s > 0; s /= 3) {
                if(tid < s && tid + 2 * s < blockDim.x) {
                        int list[3];
                        list[0] = sdata[tid], list[1] = sdata[tid + s], list[2] = sdata[tid + 2 * s];
                        if(list[1] < list[0])
                                swapGpu(list[1], list[0]);
                        if(list[2] < list[0])
                                swapGpu(list[2], list[0]);
                        if(list[2] < list[1])
                                swapGpu(list[2], list[1]);

                        sdata[tid] = list[1];
                }

                __syncthreads();
        }

        *med = sdata[0];
}

__global__ void gpuMin(int *entries, int *min, int numEntries) {
        extern __shared__ int sdata[];

        int tid = threadIdx.x;
        int i = blockIdx.x * (blockDim.x * 2) + threadIdx.x;

        if(i + blockDim.x < numEntries)
                sdata[tid] = (entries[i] < entries[i + blockDim.x]) ? entries[i] : entries[i + blockDim.x];
        __syncthreads();

        for(int s = blockDim.x / 2; s > 0; s >>= 1) {
                if(tid < s)
                        sdata[tid] = (sdata[tid] < sdata[tid + s]) ? sdata[tid] : sdata[tid + s];

                __syncthreads();
        }

        *min = sdata[0];
}

__global__ void gpuMax(int *entries, int *max, int numEntries) {
        extern __shared__ int sdata[];

        int tid = threadIdx.x;
        int i = blockIdx.x * (blockDim.x * 2) + threadIdx.x;

        if(i + blockDim.x < numEntries)
                sdata[tid] = (entries[i] > entries[i + blockDim.x]) ? entries[i] : entries[i + blockDim.x];
        __syncthreads();

        for(int s = blockDim.x / 2; s > 0; s >>= 1) {
                if(tid < s)
                        sdata[tid] = (sdata[tid] > sdata[tid + s]) ? sdata[tid] : sdata[tid + s];

            __syncthreads();
    }

    *max = sdata[0];
}

int partition(int *entries, int left, int right, int pivotIdx) {
        int i, storeIdx = left, pivot = entries[pivotIdx];

        swap(entries[pivotIdx], entries[right]);

        for(i = left; i < right; i++)
                if(entries[i] < pivot) {
                        swap(entries[i], entries[storeIdx]);
                        storeIdx++;
                }

        return storeIdx;
}

int cpuSelect(int *entries, int left, int right, int k) {
        if(left == right)
                return entries[left];

        int pivotIdx = ((left + right) >> 2) + 1, pivotNewIdx, pivotDist;

        pivotNewIdx = partition(entries, left, right, pivotIdx);

        pivotDist = pivotNewIdx - left + 1;

        if(pivotDist == k)
                return entries[pivotNewIdx];

        else if(k < pivotDist)
                return cpuSelect(entries, left, pivotNewIdx - 1, k);

        else
                return cpuSelect(entries, pivotNewIdx + 1, right, k - pivotDist);
}

int main(int argc, char *argv[]) {
        if(argc != 3) {
                std::cout << "ERROR: Incorrect number of input arguments" << std::endl;
                std::cout << "Proper usage: " << argv[0] << " fileName numEntries" << std::endl;
                exit(1);
        }

        std::ifstream inp(argv[1]);

        if(!inp.is_open()) {
                std::cout << "ERROR: File I/O error" << std::endl;
                std::cout << "Could not find file " << argv[1] << std::endl;
                exit(2);
        }

        int numEntries = atoi(argv[2]), i = 0;

        int *entries = new int[numEntries];

        while(inp >> entries[i] && i < numEntries)
                i++;

        if(i < numEntries) {
                std::cout << "ERROR: File I/O error" << std::endl;
                std::cout << "Command-line input suggested " << numEntries << " entries, but only found " << i << " entries" << std::endl;
                exit(2);
        }

        if(inp >> i) {
                std::cout << "ERROR: File I/O error" << std::endl;
                std::cout << "Command-line input suggested " << numEntries << " entries, but file contains more entries" << std::endl;
                exit(2);
        }

        int min, max;
        int *d_entries, *d_min, *d_max;

        checkCudaErrors(cudaMalloc(&d_entries, sizeof(int) * numEntries));
        checkCudaErrors(cudaMalloc(&d_min, sizeof(int)));
        checkCudaErrors(cudaMalloc(&d_max, sizeof(int)));

        checkCudaErrors(cudaMemcpy(d_entries, entries, sizeof(int) * numEntries, cudaMemcpyHostToDevice));

        gpuMin<<<16, numEntries / 16, numEntries / 16 * sizeof(int)>>>(d_entries, d_min, numEntries);
        getLastCudaError("kernel launch failure");
        gpuMax<<<16, numEntries / 16, numEntries / 16 * sizeof(int)>>>(d_entries, d_max, numEntries);
        getLastCudaError("kernel launch failure");

        checkCudaErrors(cudaMemcpy(&min, d_min, sizeof(int), cudaMemcpyDeviceToHost));
        checkCudaErrors(cudaMemcpy(&max, d_max, sizeof(int), cudaMemcpyDeviceToHost));

        std::cout << "The minimum value is: " << min << std::endl;
        std::cout << "The maximum value is: " << max << std::endl;

        if(numEntries % 2) {
                int med, *d_med;
                checkCudaErrors(cudaMalloc(&d_med, sizeof(int)));

                gpuMedOdd<<<16, numEntries / 16, 16 * sizeof(int)>>>(d_entries, d_med, numEntries);
                getLastCudaError("kernel launch failure");

                checkCudaErrors(cudaMemcpy(&med, d_med, sizeof(int), cudaMemcpyDeviceToHost));

                std::cout << "The median value is: " << med << std::endl;
        }
        else {
                int *d_med;
                cudaMalloc(&d_med, sizeof(int));
                gpuMedOdd<<<16, numEntries / 16>>>(d_entries, d_med, numEntries);
        }

        min = cpuMin(entries, numEntries);

        max = cpuMax(entries, numEntries);

        if(numEntries % 2) {
                int median = cpuSelect(entries, 0, numEntries - 1, (numEntries - 1) / 2 + 1);
                std::cout << "The median value is: " << median << std::endl;
        }

        else {
                int med2 = cpuSelect(entries, 0, numEntries - 1, numEntries / 2);
                int med1 = cpuSelect(entries, 0, numEntries - 1, numEntries / 2 + 1);

                float median = 0.5 * (med1 + med2);
                std::cout << "The median value is: " << median << std::endl;
        }

        std::cout << "The minimum value is: " << min << std::endl;
        std::cout << "The maximum value is: " << max << std::endl;

        exit(0);
}
#包括
#包括
#包括
#定义校验错误(err)\校验错误(err、\文件、\行)
#定义GetLastCudError(msg)\ GetLastCudError(msg、\文件、\行)
内联无效校验CUDAERRORS(cudaError err,const char*文件,const int行){
如果(cudaSuccess!=错误){

std::cout跳出的一件事是您的共享内存大小没有设置;也就是说,您声明您的共享内存为

extern __shared__ int sdata[];
但是,当您调用内核时,启动参数是

gpuMedOdd<<<9, numEntries / 9>>>(...)
其中,
smem\u in\u bytes
是内核共享内存的大小。如果不指定大小,它将默认为0。因此,在当前代码中,
\u shared\u\u
内存数组
sdata
的长度将为0字节

编辑:以下是CUDA编程指南相关部分的链接:

我在您的代码中看到的一个问题是,您似乎颠倒了启动参数:

gpuMedOdd<<<16, numEntries / 16, 16 * sizeof(int)>>>(d_entries, d_med, numEntries);
gpumedod(数据项、数据项、数字项);
我想你打算:

gpuMedOdd<<< numEntries/16, 16, 16 * sizeof(int)>>>(d_entries, d_med, numEntries);
gpuMedOdd>(数据项、数据项、数字项);
第一个启动参数是每个网格的块数。第二个启动参数是每个块的线程数。这里我假设您希望每个块启动16个线程。如果实际上您的目的是启动固定数量的块(16)如果每个块的线程数根据输入大小而变化,那么我认为这不是典型的好的cuda编码,如果输入大小太大,它会爆炸,因为你会超过每个块的最大线程数限制。另外,因为你的共享内存分配是固定的(64字节),我假设您希望每个块有固定数量的线程


我的另一个建议是,与其只报告“CUDA运行时错误”,还不如分析返回的错误代码。看看我已经提到的错误代码。

感谢您的响应。我已将内核调用更改为gpuMedOdd,但仍然得到相同的结果(即0)@wolfPack88共享内存是每个块的,因此如果您有一个线程处理一个元素,那么您需要(numEntries/9)*sizeof(int)对不起,输入错误:我的意思是我将内核调用更改为gpuMedOdd,得到了相同的结果(我认为每个线程处理3个元素)@wolfPack88啊,但是您有1个线程将1个元素加载到共享内存中,所以您将有numEntries/9个线程将那么多元素加载到共享内存中是使用少量线程启动,这些线程带有易于识别的输入数据,并在通过sdata[tid]=entries[i];uu syncthreads()加载后打印出共享内存;以确保获取所有数据。显然,sdata只包含零。这非常有趣,因为我使用了直接位于此项下的另一个内核调用(使用相同的d_项)为了找到最小值,并输出正确的答案……您在所有cuda调用和内核调用上都做了什么?公平地说,我没有对cuda调用进行错误检查,但正如我在下面的评论中提到的,我在不同的内核调用中使用相同的数据(找到数组最小值的调用)并得到正确的结果。我假设这意味着数据已正确复制到设备上。唯一的问题是内核本身,对吗?我围绕您的代码构建了一个简单的程序,包括您在下面讨论的内核调用更改,以说明共享内存。我没有更改您编写的任何代码,我写的程序对我来说是正确的(我得到了一个非零的结果)。因此,我认为在您发布的代码之外存在一个问题,我的猜测是,如果您进行严格的错误检查,您会发现它。例如,可能您将cudaMemcpy的一个参数弄错了。当您解析cudaMemcpy调用中的错误代码时,返回的错误字符串是什么?您将在内核之后执行此调用调用。这可能是内核调用的一个残余错误。你对numEntries有什么价值?与其问20个问题,不如让我给你一些建议。1.)这个程序不会那么长。发布你正在使用的全部代码,并使其简单可编译。2.)与其在这些注释中发布代码行或代码段,不如编辑你的原始代码向他们提问。这使其他人更容易阅读,也更连贯。除了@RobertCrovella的答案外,您可能已经注意到了一些注释;如果您的numEntries是偶数,那么main()中的代码仍然是错误的,并且您从未释放cuda分配的内存,因此您存在内存泄漏
gpuMedOdd<<<9, numEntries / 9, smem_in_bytes>>>(...)
gpuMedOdd<<<16, numEntries / 16, 16 * sizeof(int)>>>(d_entries, d_med, numEntries);
gpuMedOdd<<< numEntries/16, 16, 16 * sizeof(int)>>>(d_entries, d_med, numEntries);