一个通用的warp级std :: copy-like函数 - 它应该占什么?

问题描述 投票:-1回答:1

C ++标准库使用以下代码实现std::copy(忽略所有类型的包装和概念检查等)和简单的循环:

for (; __first != __last; ++__result, ++__first)
    *__result = *__first;

现在,假设我想要一个用于warp(而不是块;而不是网格)的通用std::copy函数来用于协同地将数据从一个地方复制到另一个地方。我们甚至假设该函数采用指针而不是任意迭代器。

当然,在CUDA中编写通用代码通常是一种无用的追求 - 因为我们可能首先牺牲使用GPU的许多好处而支持通用性 - 所以我将允许自己一些布尔/枚举模板可能在频繁出现的情况之间进行选择的参数,避免运行时检查。因此签名可能是:

template <typename T, bool SomeOption, my_enum_t AnotherOption>
T* copy(
    T*        __restrict__  destination, 
    const T*  __restrict__  source,
    size_t                  length
); 

但是对于这些情况中的每一种情况,我都希望获得最佳性能(或者考虑到我们不知道其他经线正在做什么的最佳预期性能)。

在编写这样的函数时,我应该考虑哪些因素?换句话说:在实现这个功能时,我应该区分哪些情况?

笔记:

  • 这应该针对Compute Capabilities 3.0或更高版本(即Kepler或更新的微架构)
  • 我不想做一个运行时API memcpy()调用。至少,我不认为我这样做。
optimization cuda gpgpu stl-algorithm
1个回答
-1
投票

我认为应该考虑的因素:

  • 合并内存写入 - 确保warp中的连续通道写入连续的内存位置(无间隙)。
  • 类型大小与内存事务大小I - 如果sizeof(T)sizeof(T)是1或2,并且我们已经让每个通道写入单个元素,整个warp将写入小于128B,浪费一些内存事务。相反,我们应该让每个线程在寄存器中放置2或4个输入元素,然后编写它
  • 类型大小与内存事务大小II - 对于类型大小,使lcm(4,sizeof(T))> 4,它不是很清楚该怎么做。当每个通道写入超过4个字节时,编译器/ GPU处理写入的程度如何?我想知道。
  • 由于一次读取多个元素而导致松弛 - 如果每个线程希望为每次写入读取2或4个元素,并写入4字节整数 - 我们可能在输入的开头和结尾有1或2个元素必须单独处理。
  • 由于输入地址未对齐导致的松弛 - 在32B事务中读取输入(在合理的假设下);因此,我们必须处理第一个元素直到32B的倍数,并且最后一个元素(在最后一个这样的倍数之后)不同。
  • 由于输出地址不对齐导致的松弛 - 输出写入高达128B的事务(或者它只是32B?);因此,我们必须处理第一个元素,直到这个数字的倍数,以及最后一个元素(在最后一个这样的倍数之后),不同。
  • T是否可以简单地复制构造。但我们假设它是。

但可能是因为我缺少一些考虑因素,或者上述某些因素是多余的。

我一直在想的因素:

  • 块大小(即有多少其他经线)
  • 计算能力(假设它至少为3)
  • 源/目标是否在共享内存/常量内存中
  • 选择缓存模式
© www.soinside.com 2019 - 2024. All rights reserved.