C++ 在CUDA中的表现

C++ 在CUDA中的表现,c++,performance,cuda,C++,Performance,Cuda,我似乎无法找出影响内核性能的潜在因素。我实现了两个简单的内核,一个加载两个图像并将其逐像素相加,另一个加载两个图像并对其进行逐位运算。现在,我已经对它们进行了模板化,以便内核可以拍摄8位和32位图像,以及1、3和4通道图像 因此,最初我让两个内核都以uchar3和float3的形式加载全局内存,以及uchar4等。但是,由于合并,我不太确定是否要使用三元组,所以我想我应该对其进行一次分析。我认为,由于操作与通道编号无关,因此我可以像读取三倍宽度的单通道uchar图像一样读取图像,而不是真正的uc

我似乎无法找出影响内核性能的潜在因素。我实现了两个简单的内核,一个加载两个图像并将其逐像素相加,另一个加载两个图像并对其进行逐位运算。现在,我已经对它们进行了模板化,以便内核可以拍摄8位和32位图像,以及1、3和4通道图像

因此,最初我让两个内核都以
uchar3
float3
的形式加载全局内存,以及
uchar4
等。但是,由于合并,我不太确定是否要使用三元组,所以我想我应该对其进行一次分析。我认为,由于操作与通道编号无关,因此我可以像读取三倍宽度的单通道
uchar
图像一样读取图像,而不是真正的
uchar3
图像

事实上,
uchar3
全局加载比
uchar
加载慢得多。我的努力得到了证实。但是,唉,这只发生在算术内核上。按位AND运算显示了完全相反的结果

现在,我知道我可以将图像数据加载为
uint
s,而不是
uchar
s,用于按位操作,这应该可以完美地处理合并。但让我们假设我只是想学习和理解正在发生的事情

让我们忘记
float3
s和
float4
s等。我的问题是
uchar
版本的内核。那么,简而言之,为什么
uchar
加载有时比
uchar3
加载快,有时又不快

我使用的是GTX 470,计算能力2.0

另外,根据CUDA编程指南,逻辑操作和add操作具有相同的吞吐量。(我的内核实际上必须首先将
uchar
s转换为
uint
s,但这应该发生在两个内核中。)因此,从我收集的数据来看,执行长度应该大致相同

算术添加内核(
uchar
version):


和内核类似。(老实说,我不确定我是否准确地记得内核……我明天会确认)。

uchar3
由于SM的指令集中没有24位加载,因此编译器将加载拆分为单独的加载。因此,它们从未合并。在某种程度上,缓存将缓解这一问题

但是,根据具体的执行配置,每个线程可能只有大约10.7字节的缓存(您的示例可能接近该值,因为内核很简单,所以很多线程可以在一个SM上并发运行)。由于缓存不是完全关联的,在抖动发生之前,每个线程的可用字节数可能会小得多。具体发生的时间取决于许多因素,包括指令的精确调度,即使对于具有相同记录吞吐量的指令,也可能有所不同

您可以比较两个版本的
cuobjdump-sass
可执行文件的输出,以查看编译器的静态调度是否相同。然而,运行时动态调度的工作原理基本上是不可观察的


正如您所注意到的,图像的所有通道都以相同的方式进行处理,因此在线程之间如何分配它们并不重要。您最好的选择是使用
uchar4
而不是
uchar3
uchar
,这(假设图像适当对齐)将提供独立于缓存的联合访问。这将导致更短和更一致的执行时间。

您能给我们看看您的内核吗?对于未知的代码很难推理。也许可以告诉我们执行时间的比较情况(也就是说,内核在一个版本或另一个版本中所花费的时间是否相似,哪个版本更快,…),你能给出一个简洁的问题吗?您想知道为什么加载一堆
uchar
比加载一堆
uchar3
更快?CUDA 5中的探查器将发出通知,如果未平衡的加载/存储是一个问题,即使对于最基本类型的探查运行也是如此。关于这两种情况,它在百分比上说了什么?负载是线性的,并且完美地结合在一起,至少在uchar的情况下是这样。我现在不在上班,所以无法粘贴内核。我想知道为什么它有时快,有时不快。
__global__ void add_8uc1(uchar* inputOne, uchar* inputTwo, uchar* output, unsigned int width, unsigned int height, unsigned int widthStep)
{
    const int xCoordinateBase = blockIdx.x * IMAGE_X * IMAGE_MULTIPLIER + threadIdx.x;
    const int yCoordinate = blockIdx.y * IMAGE_Y + threadIdx.y;

    if (yCoordinate >= height)
        return;

#pragma unroll IMAGE_MULTIPLIER
    for (int i = 0; i < IMAGE_MULTIPLIER && xCoordinateBase + i * IMAGE_X < width; ++i)
    {
        //  Load memory.
        uchar* inputElementOne = (inputOne + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x));
        uchar* inputElementTwo = (inputTwo + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x));

        //  Write output.
        *(output + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x)) = inputElementOne[0] + inputElementTwo[0];
    }
}
__global__ void and_8uc1(uchar* inputOne, uchar* inputTwo, uchar* output, unsigned int width, unsigned int height, unsigned int widthStep)
{
    const int xCoordinateBase = blockIdx.x * IMAGE_X * IMAGE_MULTIPLIER + threadIdx.x;
    const int yCoordinate = blockIdx.y * IMAGE_Y + threadIdx.y;

    if (yCoordinate >= height)
        return;

#pragma unroll IMAGE_MULTIPLIER
    for (int i = 0; i < IMAGE_MULTIPLIER && xCoordinateBase + i * IMAGE_X < width; ++i)
    {
        //  Load memory.
        uchar* inputElementOne = (inputOne + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x));
        uchar* inputElementTwo = (inputTwo + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x));

        //  Write output.
        *(output + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x)) = inputElementOne[0] & inputElementTwo[0];
    }
}
        //  Load memory.
    uchar3 inputElementOne = *reinterpret_cast<uchar3*>(inputOne + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x) * 3);
    uchar3 inputElementTwo = *reinterpret_cast<uchar3*>(inputTwo + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x) * 3);

    //  Write output.
    *reinterpret_cast<uchar3*>(output + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x) * 3) 
        = make_uchar3(inputElementOne.x + inputElementTwo.x, inputElementOne.y + inputElementTwo.y, inputElementOne.z + inputElementTwo.z);