GPU 上所需的计算内存比 (OP/B)
Desired Compute-To-Memory-Ratio (OP/B) on GPU
我想了解 GPU 的架构以及我们如何评估我们的程序在 GPU 上的性能。我知道申请可以是:
- 计算限制: 性能受 FLOPS 速率限制。处理器的核心得到充分利用(总是有工作要做)
内存限制:性能受内存限制
带宽。处理器的内核经常空闲,因为内存无法足够快地提供数据
下图显示了每个微架构的 FLOPS 速率、峰值内存带宽和所需的计算内存比,标记为 (OP/B)。
我还有一个如何计算此 OP/B 指标的示例。示例:下面是用于应用矩阵-矩阵乘法的 CUDA 代码的一部分
for(unsigned int i = 0; i < N; ++i) {
sum += A[row*N + i]*B[i*N + col];
}
而这个矩阵乘法计算OP/B的方法如下:
- 矩阵乘法执行0.25OP/B
- 每加载 2 个 FP 值 (8B),增加 1 个 FP 和 1 个 FP mul
- 忽略商店
如果我们想利用它:
- 但是矩阵乘法具有很高的重用潜力。对于 NxN 矩阵:
- 加载的数据:(2个输入矩阵)×(N^2个值)×(4 B)= 8N^2 B
- 操作:(N^2 点积)(N adds + N muls each) = 2N^3 OP
- 潜在计算内存比:0.25N OP/B
所以如果我清楚地理解这一点,我有以下问题:
- 总是OP/B越大越好?
- 我们怎么知道我们有多少 FP 操作?是加法还是乘法
- 我们如何知道每个 FP 操作加载了多少字节?
It is always the case that the greater OP/B, the better ?
不总是。目标值平衡计算管道吞吐量和内存管道吞吐量的负载(即 op/byte 的级别意味着两个管道都将满载)。当您将 op/byte 增加到超出该级别或某个级别时,您的代码将从平衡切换到计算绑定。一旦您的代码受计算限制,性能将由作为限制因素的计算管道决定。超出此点的额外 op/byte 增加可能不会影响代码性能。
how do we know how much FP operations we have ? Is it the adds and the multiplications
是的,对于您显示的简单代码,它是加法和乘法。其他更复杂的代码可能有其他因素(例如 sin
、cos
等)也可能有所贡献。
作为 "manually counting" FP 操作的替代方法,GPU 分析器可以指示代码已执行的 FP 操作数。
how do we know how many bytes are loaded per FP operation ?
与上一个问题类似,对于简单的代码,您可以"manually count"。对于复杂的代码,您可能希望尝试使用分析器功能进行估算。对于您显示的代码:
sum += A[row*N + i]*B[i*N + col];
必须加载 A
和 B
中的值。如果它们是 float
个数量,那么它们每个都是 4 个字节。总共是 8 个字节。该行代码将需要 1 个浮点乘法 (A * B) 和一个浮点加运算 (sum +=)。编译器会将它们融合成一条指令(融合乘加),但最终效果是您每 8 个字节执行两个浮点运算。 op/byte 是 2/8 = 1/4。在这种情况下,循环不会改变比率。要增加这个数字,您需要探索各种优化方法,例如 a tiled shared-memory matrix multiply,或者只使用 CUBLAS。
(像 row*N + i
这样的操作是 整数 算术,不会影响浮点负载,尽管它们可能在性能方面很重要。 )
我想了解 GPU 的架构以及我们如何评估我们的程序在 GPU 上的性能。我知道申请可以是:
- 计算限制: 性能受 FLOPS 速率限制。处理器的核心得到充分利用(总是有工作要做)
内存限制:性能受内存限制 带宽。处理器的内核经常空闲,因为内存无法足够快地提供数据
下图显示了每个微架构的 FLOPS 速率、峰值内存带宽和所需的计算内存比,标记为 (OP/B)。
我还有一个如何计算此 OP/B 指标的示例。示例:下面是用于应用矩阵-矩阵乘法的 CUDA 代码的一部分
for(unsigned int i = 0; i < N; ++i) {
sum += A[row*N + i]*B[i*N + col];
}
而这个矩阵乘法计算OP/B的方法如下:
- 矩阵乘法执行0.25OP/B
- 每加载 2 个 FP 值 (8B),增加 1 个 FP 和 1 个 FP mul
- 忽略商店
如果我们想利用它:
- 但是矩阵乘法具有很高的重用潜力。对于 NxN 矩阵:
- 加载的数据:(2个输入矩阵)×(N^2个值)×(4 B)= 8N^2 B
- 操作:(N^2 点积)(N adds + N muls each) = 2N^3 OP
- 潜在计算内存比:0.25N OP/B
所以如果我清楚地理解这一点,我有以下问题:
- 总是OP/B越大越好?
- 我们怎么知道我们有多少 FP 操作?是加法还是乘法
- 我们如何知道每个 FP 操作加载了多少字节?
It is always the case that the greater OP/B, the better ?
不总是。目标值平衡计算管道吞吐量和内存管道吞吐量的负载(即 op/byte 的级别意味着两个管道都将满载)。当您将 op/byte 增加到超出该级别或某个级别时,您的代码将从平衡切换到计算绑定。一旦您的代码受计算限制,性能将由作为限制因素的计算管道决定。超出此点的额外 op/byte 增加可能不会影响代码性能。
how do we know how much FP operations we have ? Is it the adds and the multiplications
是的,对于您显示的简单代码,它是加法和乘法。其他更复杂的代码可能有其他因素(例如 sin
、cos
等)也可能有所贡献。
作为 "manually counting" FP 操作的替代方法,GPU 分析器可以指示代码已执行的 FP 操作数。
how do we know how many bytes are loaded per FP operation ?
与上一个问题类似,对于简单的代码,您可以"manually count"。对于复杂的代码,您可能希望尝试使用分析器功能进行估算。对于您显示的代码:
sum += A[row*N + i]*B[i*N + col];
必须加载 A
和 B
中的值。如果它们是 float
个数量,那么它们每个都是 4 个字节。总共是 8 个字节。该行代码将需要 1 个浮点乘法 (A * B) 和一个浮点加运算 (sum +=)。编译器会将它们融合成一条指令(融合乘加),但最终效果是您每 8 个字节执行两个浮点运算。 op/byte 是 2/8 = 1/4。在这种情况下,循环不会改变比率。要增加这个数字,您需要探索各种优化方法,例如 a tiled shared-memory matrix multiply,或者只使用 CUBLAS。
(像 row*N + i
这样的操作是 整数 算术,不会影响浮点负载,尽管它们可能在性能方面很重要。 )