9.1_概述
9.1 概述
多GPU系统通常包含多个GPU主板。如第2.3节中描述的那样,这些GPU主板可能带有一个PCIe桥接芯片(例如GeForce GTX 690),或者插在多个PCIe插槽上,或者两种情况兼而有之。多GPU系统的每个GPU被PCIe总线分离开来,所以连接到本地GPU(它的设备内存)的内存带宽和连接到其他的GPU以及CPU的内存带宽之间有巨大差距。
许多属于多GPU的CUDA功能,例如点对点寻址,要求这些GPU具有相同规格。对于那些可以指定目标硬件的应用程序(如为特定硬件配置定制的垂直应用程序),这一要求是没问题的。但当硬件系统包含各式GPU时(例如,同时配有日常使用低配置显卡和强大的游戏卡),应用程序可能需要使用启发式算法来决定使用哪个GPU,或者在这些GPU间进行负载平衡,以使强大的GPU做更多的计算工作。
对所有使用多GPU的CUDA应用来说,可分享锁页内存是关键因素。正如5.1.2小节所述,可分享锁页内存也是锁页内存,只是它映射给所有的CUDA上下文,这样任何GPU都可以直接读取或写入该内存。
CPU线程模型
CUDA 4.0以前,驱动多个GPU的唯一的办法是分别为它们创建一个CPU线程。在每个CPU线程中,必须在执行任何CUDA代码之前调用一次CUDASetDevice()函数。当CPU线程开始启动CUDA之际,此举意在告诉CUDA应该初始化哪个设备。无论是哪个CPU线程发出函数调用,该线程将获得独占访问GPU的权利,因为CUDA驱动程序尚无法做到线程安全(thread-safe),无法支持多个线程同时访问同一个GPU。
在CUDA4.0中,CUDASetDevice()函数做了修改,实现了大家先前预期的语义:它告诉CUDA哪个GPU应该执行后续的CUDA操作。若有多个线程同时对同一个GPU进行操作,应该能够正确工作,只是可能会产生轻微的性能损失。对于我们的N-体示例程序,只使用一个CPU线程逐次在给定的设备上运行。多线程方案则拥有N个线程,每个线程作用在一个特定设备上;相应的,单线程方案是一个线程轮番作用于N个设备。