Parallel processing 在CUDA中连续填充设备阵列

Parallel processing 在CUDA中连续填充设备阵列,parallel-processing,cuda,Parallel Processing,Cuda,(这可能更像是一个理论上的并行优化问题,而不是CUDA特定的问题本身。一般来说,我对并行编程非常陌生,所以这可能只是个人的无知。) 我有一个由64位二进制数组成的工作负载,我在其上运行分析。如果分析成功完成,则该二进制数为“有效解决方案”。如果分析中途中断,则数字为“无效”。最终目标是获得所有有效解决方案的列表 现在,我正在分析的64位二进制数有数万亿个,但只有~5%或更少是有效的解决方案,而且它们通常是成束出现的(即,每连续1000个数字有效,然后每随机十亿左右无效)。我找不到束之间空间的模式

(这可能更像是一个理论上的并行优化问题,而不是CUDA特定的问题本身。一般来说,我对并行编程非常陌生,所以这可能只是个人的无知。)

我有一个由64位二进制数组成的工作负载,我在其上运行分析。如果分析成功完成,则该二进制数为“有效解决方案”。如果分析中途中断,则数字为“无效”。最终目标是获得所有有效解决方案的列表

现在,我正在分析的64位二进制数有数万亿个,但只有~5%或更少是有效的解决方案,而且它们通常是成束出现的(即,每连续1000个数字有效,然后每随机十亿左右无效)。我找不到束之间空间的模式,所以我不能忽略大量无效的解决方案

目前,内核调用中的每个线程只分析一个数字。如果该数字有效,则在设备阵列的相应位置表示该数字。如果无效也一样。所以基本上,我为分析的值生成一个数据点,不管它是否有效。然后,一旦阵列已满,仅当找到有效的解决方案(由设备上的标志表示)时,我才会将其复制到主机。这样,当阵列的大小与网格中的线程的大小相同时,总吞吐量最大

但是从时间上看,将内存复制到GPU和从GPU复制内存是非常昂贵的。也就是说,我想做的是仅在必要时复制数据;我希望仅使用有效的解决方案填充设备阵列,然后在阵列已满后,将其从主机复制过来。但是如何在并行环境中连续填充数组呢?还是我处理这个问题的方式不对

编辑1

这是我最初开发的内核。如您所见,我为每个分析的值生成1字节的数据。现在我只需要每个有效的64位数字;如果需要,我可以制作一个新内核。正如一些评论员所建议的,我目前正在研究流压缩

__global__ void kValid(unsigned long long*kInfo, unsigned char*values, char *solutionFound) {
    //a 64 bit binary value to be evaluated is called a kValue
    unsigned long long int kStart, kEnd, kRoot, kSize, curK;
    //kRoot is the kValue at the start of device array, this is used is the device array is larger than the total threads in the grid
    //kStart is the kValue to start this kernel call on
    //kEnd is the last kValue to validate
    //kSize is how many bits long is kValue (we don't necessarily use all 64 bits but this value stays constant over the entire chunk of values defined on the host
    //curK is the current kValue represented as a 64 bit unsigned integer

    int rowCount, kBitLocation, kMirrorBitLocation, row, col, nodes, edges; 

    kStart = kInfo[0];
    kEnd = kInfo[1];
    kRoot = kInfo[2];
    nodes = kInfo[3];
    edges = kInfo[4];
    kSize = kInfo[5];

    curK = blockIdx.x*blockDim.x + threadIdx.x + kStart;
    if (curK > kEnd) {//check to make sure you don't overshoot the end value
        return;
    }

    kBitLocation = 1;//assuming the first bit in the kvalue has a position 1;
    for (row = 0; row < nodes; row++) {
        rowCount = 0;
        kMirrorBitLocation = row;//the bit position for the mirrored kvals is always starts at the row value (assuming the first row has a position of 0)
        for (col = 0; col < nodes; col++) {
            if (col > row) {
                if (curK & (1 << (unsigned long long int)(kSize - kBitLocation))) {//add one to kIterator to convert to counting space
                    rowCount++;
                }
                kBitLocation++;
            }
            if (col < row) {
                if (col > 0) {
                    kMirrorBitLocation += (nodes - 2) - (col - 1);
                }
                if (curK & (1 << (unsigned long long int)(kSize - kMirrorBitLocation))) {//if bit is set
                    rowCount++;
                }
            }
        }
        if (rowCount != edges) {
            //set the ith bit to zero
            values[curK - kRoot] = 0;
            return;
        }
    }
    //set the ith bit to one
    values[curK - kRoot] = 1;
    *solutionFound = 1; //not a race condition b/c it will only ever be set to 1 by any thread.
}
\uuuu全局\uuuuu无效kValid(无符号long long*kInfo、无符号char*值、char*solutionFound){
//要计算的64位二进制值称为kValue
未签名长整型kStart、kEnd、kRoot、kSize、curK;
//kRoot是设备阵列开始时的kValue,当设备阵列大于网格中的总线程时,使用kValue
//kStart是启动此内核调用的kValue
//kEnd是最后一个要验证的kValue
//kSize是kValue的长度(我们不一定使用所有64位,但该值在主机上定义的整个值块上保持不变)
//curK是表示为64位无符号整数的当前kValue
int rowCount、kBitLocation、kMirrorBitLocation、行、列、节点、边;
kStart=kInfo[0];
kEnd=kInfo[1];
kRoot=kInfo[2];
节点=kInfo[3];
边=kInfo[4];
kSize=kInfo[5];
curK=blockIdx.x*blockDim.x+threadIdx.x+kStart;
如果(curK>kEnd){//请检查以确保没有超出最终值
返回;
}
kBitLocation=1;//假设kvalue中的第一位的位置为1;
对于(行=0;行<节点;行++){
行数=0;
kMirrorBitLocation=row;//镜像kvals的位位置始终从行值开始(假设第一行的位置为0)
for(col=0;col行){
如果(凝块和(10){
kMirrorBitLocation+=(节点-2)-(列-1);
}
if(curK&(1)(此答案假设输出顺序不重要,有效值的位置也不重要。)

从概念上讲,您的分析会生成一组有效值。您描述的实现使用了该集的密集表示:每个潜在值对应一位。但您指出数据非常稀疏(5e-2或1000/10^9=1e-6);此外,跨PCI express复制数据是一件非常痛苦的事情

那么,为什么不考虑一个稀疏表示?最简单的一个将仅仅是一个无序的有效值序列。当然,需要在线程之间进行一些同步(甚至是跨块)的写入。粗略地,可以将经卷在共享内存中收集它们的有效值;然后在块级同步到COLLE。ct块的有效值(对于它已分析的给定输入块);最后使用原子学从所有块收集数据

哦,还有-让每个线程分析多个值,这样就不必进行太多的同步。

(这个答案假设输出顺序是无关紧要的,有效值的位置也是无关紧要的。)

从概念上讲,您的分析会生成一组有效值。您描述的实现使用了该集的密集表示:每个潜在值对应一位。但您指出数据非常稀疏(5e-2或1000/10^9=1e-6);此外,跨PCI express复制数据是一件非常痛苦的事情

那么,为什么不考虑一个稀疏表示?最简单的一个将仅仅是一个无序的有效值序列。当然,需要在线程之间进行一些同步(甚至是跨块)的写入。粗略地,可以将经卷在共享内存中收集它们的有效值;然后在块级同步到COLLE。ct块的有效值(对于它已分析的给定输入块);最后使用原子学从所有块收集数据


哦,还有-让每个线程分析多个值,这样你就不必进行太多的同步。

因此,在计算返回之前,你需要让每个线程分析多个数字(数千或数百万)。因此,如果你在线程中分析一百万个数字,你只需要%5的空间