感谢昨天的答案here,我想我现在已经使用Pascal 1080Ti对统一内存进行了正确的基本测试。它分配一个50GB的单维数组并将其加起来。如果我理解正确,它应该是内存绑定,因为这个测试很简单(添加整数)。但是,它需要24秒,相当于大约2GB / s。当我运行CUDA8 bandwidthTest时,我看到更高的速率:11.7GB / s固定和8.5GB / s可分页。
有没有办法让测试运行速度超过24秒?
以下是完整的测试代码:
$ pgcc -fast -acc -ta=tesla:managed:cc60 -Minfo=accel firstAcc.c
main:
40, Accelerator kernel generated
Generating Tesla code
40, Generating reduction(+:sum)
41, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */
40, Generating implicit copyin(a[:13421772800])
我按如下方式编译:
$ ./a.out
n = 13421772800, GB = 50.000
Initializing ... done in 36.082607 (single CPU thread)
Sum is -5 and it took 23.902612
$ ./a.out
n = 13421772800, GB = 50.000
Initializing ... done in 36.001578 (single CPU thread)
Sum is -5 and it took 24.180615
然后我跑了两次:
$ ./bandwidthTest --memory=pageable
[CUDA Bandwidth Test] - Starting...
Running on...
Device 0: GeForce GTX 1080 Ti
Quick Mode
Host to Device Bandwidth, 1 Device(s)
PAGEABLE Memory Transfers
Transfer Size (Bytes) Bandwidth(MB/s)
33554432 8576.7
Device to Host Bandwidth, 1 Device(s)
PAGEABLE Memory Transfers
Transfer Size (Bytes) Bandwidth(MB/s)
33554432 11474.3
Device to Device Bandwidth, 1 Device(s)
PAGEABLE Memory Transfers
Transfer Size (Bytes) Bandwidth(MB/s)
33554432 345412.1
Result = PASS
NOTE: The CUDA Samples are not meant for performance measurements. Results may vary when GPU Boost is enabled.
结果(-5)是正确的,因为我以这种方式设置数据。这些数字是7个整数-3:+3的重复序列,当它们相加时,除了结尾处的2的余数之外全部抵消(-3 -2 = -5)。
可分页的bandwidthTest(CUDA 8 samples / 1_Utilities)结果是:
$ ./bandwidthTest --memory=pageable --mode=shmoo
[CUDA Bandwidth Test] - Starting...
Running on...
Device 0: GeForce GTX 1080 Ti
Shmoo Mode
.................................................................................
Host to Device Bandwidth, 1 Device(s)
PAGEABLE Memory Transfers
Transfer Size (Bytes) Bandwidth(MB/s)
1024 160.3
2048 302.1
3072 439.2
4096 538.4
5120 604.6
6144 765.3
7168 875.0
8192 979.2
9216 1187.3
10240 1270.6
11264 1335.0
12288 1449.3
13312 1579.6
14336 1622.2
15360 1836.0
16384 1995.0
17408 2133.0
18432 2189.8
19456 2289.2
20480 2369.7
22528 2525.8
24576 2625.8
26624 2766.0
28672 2614.4
30720 2895.8
32768 3050.5
34816 3151.1
36864 3263.8
38912 3339.2
40960 3395.6
43008 3488.4
45056 3557.0
47104 3642.1
49152 3658.5
51200 3736.9
61440 4040.4
71680 4076.9
81920 4310.3
92160 4522.6
102400 4668.5
204800 5461.5
307200 5820.7
409600 6003.3
512000 6153.8
614400 6232.5
716800 6285.9
819200 6368.9
921600 6409.3
1024000 6442.5
1126400 6572.3
2174976 8239.3
3223552 9041.6
4272128 9524.2
5320704 9824.5
6369280 10065.2
7417856 10221.2
8466432 10355.7
9515008 10452.8
10563584 10553.9
11612160 10613.1
12660736 10680.3
13709312 10728.1
14757888 10763.8
15806464 10804.4
16855040 10838.1
18952192 10820.9
21049344 10949.4
23146496 10990.7
25243648 11021.6
27340800 11028.8
29437952 11083.2
31535104 11098.9
33632256 10993.3
37826560 10616.5
42020864 10375.5
46215168 10186.1
50409472 10085.4
54603776 10013.9
58798080 10004.8
62992384 9998.6
67186688 10006.4
我看到了这个说明。但是我应该用什么呢?这些测量结果似乎在正确的球场吗?
是否可以采取任何措施让测试在6秒(50GB / 8.5GB / s)而不是25秒内运行?
--mode = shmoo的结果实际上显示可分页达到更高的速率:11GB / s。
$ pgcc -V
pgcc 17.4-0 64-bit target on x86-64 Linux -tp haswell
PGI Compilers and Tools
Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
$ cat /usr/local/cuda-8.0/version.txt
CUDA Version 8.0.61
提前致谢。
string
答案 0 :(得分:5)
页面错误处理显然比纯数据副本更复杂。因此,当您通过页面错误将数据驱动到GPU时,它无法在性能方面与纯数据副本竞争。
页面错误实质上引入了GPU处理的另一种延迟。 GPU是一种延迟隐藏的机器,但它需要程序员给它隐藏延迟的机会。这可以粗略地描述为暴露足够的并行工作。
从表面上看,您似乎已经暴露了大量的并行工作(数据集中的~12B元素)。但是每个字节或检索到的元素的工作强度非常小,因此GPU仍然有机会隐藏与页面错误相关的延迟。换句话说,GPU具有基于可在GPU上飞行的最大线程补充(上限:2048 * SM的SM)以及在每个线程中暴露的工作来执行延迟隐藏的瞬时容量。不幸的是,在你的例子中每个线程中暴露的工作可能很小 - 基本上是一次添加。
帮助GPU延迟隐藏的一种方法是增加每个线程的工作,并且有各种技术可以做到这一点。一个好的起点是选择具有高计算复杂度的算法(如果可能)。矩阵 - 矩阵乘法是每个数据元素的大计算复杂度的经典例子。
在这种情况下,一些建议是认识到你要做的事情是非常有序的,因此从编程的角度来看,通过将工作分解成碎片并自己管理数据传输并不困难。 。这将允许您实现数据传输操作的链路的全部带宽,实现主机 - >设备带宽的近似完全利用,以及(在该示例中非常小的程度)复制和计算的重叠。对于这样一个简单且容易分解的问题,程序员不使用UM /超额订阅/页面错误是有意义的。
例如,这种方法(UM /超额订阅/页面错误)可能会发光的地方将是一种算法,其中程序员很难提前预测访问模式。遍历大图(可能不会同时在GPU内存中)可能就是一个例子。如果你有一个图遍历问题,每个边遍历有大量的工作,那么图中的页面故障跳跃节点到节点的成本可能不是什么大问题,并且简化了编程工作(不是必须明确管理图形数据移动可能是值得的。
关于预取,它是有问题的,它是否在这里有用,即使它可用。预取仍然主要取决于在预取请求正在进行时有做其他事情。如果每个要处理的数据项的工作量很少,那么一个聪明的预取方案确实会为这个例子提供很多好处并不清楚。我们可以想象可能是聪明,复杂的预取策略,但是这样的努力可能更好地用于为这样的问题制作分区显式数据传输系统。
答案 1 :(得分:2)
在2013年11月的这篇博文中:https://devblogs.nvidia.com/parallelforall/unified-memory-in-cuda-6/ NVIDIA写道
重要的一点是经过精心调整的CUDA程序,使用流和cudaMemcpyAsync 有效地将执行与数据传输重叠可能比仅使用统一内存的CUDA程序执行得更好< / strong>即可。可以理解的是:CUDA运行时从来没有像程序员那样需要数据所需的信息和时间! CUDA程序员仍然可以访问显式设备内存分配和异步内存副本,以优化数据管理和CPU-GPU并发。统一内存首先是一种生产力功能,可为并行计算提供更平滑的入口,而不会为高级用户带走任何CUDA功能。
同样在2014年3月:https://devblogs.nvidia.com/parallelforall/cudacasts-episode-18-cuda-6-0-unified-memory/
CUDA 6引入了统一内存,大大简化了GPU计算的内存管理。现在,您可以在将代码移植到GPU时专注于编写并行内核,并且内存管理成为优化。
现在,在CUDA 8中,对统一内存机制https://devblogs.nvidia.com/parallelforall/cuda-8-features-revealed/进行了一些改进。他们特别说:
重要的一点是,CUDA程序员仍然拥有在必要时明确优化数据管理和CPU-GPU并发所需的工具:CUDA 8引入了有用的API,为运行时提供内存使用提示(cudaMemAdvise())和显式预取(cudaMemPrefetchAsync())。这些工具允许与显式内存复制和固定API相同的功能,而不会回到显式GPU内存分配的限制。
所以看来您的示例可能会使用cudaMemAdvise()
/ cudaMemPrefetch()
加速。但即使如此,显式内存管理仍可能具有性能优势。
由OP添加:
通过数据位置实现性能 通过在CPU和GPU之间按需迁移数据,统一内存可以提供GPU上本地数据的性能,同时提供全球共享数据的易用性。此功能的复杂性保留在CUDA驱动程序和运行时的覆盖范围内,确保应用程序代码更易于编写。迁移的目的是从每个处理器获得全带宽; 750 GB / s的HBM2内存带宽对于提供GP100 GPU的计算吞吐量至关重要。由于GP100上的页面错误,即使对于具有稀疏数据访问的程序也可以确保局部性,其中CPU或GPU访问的页面不能提前知道,并且CPU和GPU同时访问相同阵列分配的部分。 / p>
和
由于更大的虚拟地址空间和新的页面迁移引擎,Pascal还提高了对统一内存的支持,从而实现了更高的性能,GPU内存超额配置,和系统范围的原子内存操作。