本章节翻译by weavingtime@formail.com 原文 Unified Shared Memory Allocations (intel.com)
USM(Unified Shared Memory)允许程序使用 C/C++ 指针进行内存访问。 SYCL中有三种分配内存的方式:
malloc_device:
-
分配的内存只能由指定设备访问,而不能由上下文中的其他设备或主机访问。
-
数据始终保留在设备上,因此是kernel执行最快的选择。
-
需要显式复制才能将数据传输到上下文中的主机或其他设备。
malloc_host:
-
主机和上下文中的任何其他设备都可以访问分配的内存。
-
数据始终保留在主机上,并通过 PCI 从设备进行访问。
-
与主机或设备同步数据不需要显式地拷贝。
malloc_shared:
-
分配的内存只能由主机和指定设备访问.
-
数据可以在主机和设备之间迁移(由Level Zero驱动程序操作),以实现更快的访问。
-
主机和设备之间的同步不需要显式复制,但上下文中的其他设备如果要访问则需要显示复制。
三种内存分配方式及其特性总结如下表。
内存分配方式 | 描述 | 主机是否可以访问 | 设备是否可以访问 | 内存所在位置 |
---|---|---|---|---|
主机 | 在主机的内存中分配 | 是 | 是, 可以远程通过PCIe或者fabric link访问 | 主机 |
设备 | 在设备内存中分配 | 否 | 是 | 设备 |
共享 | 在主机和设备共享的内存中分配 | 是 | 是 | 在主机和设备之间动态迁移 |
在多stack、多 slice GPU 环境中,需要注意的是,设备和共享 USM 分配与根设备相关联。 因此,同一设备上的所有stack和 slice都可以访问它们。 程序应使用根设备进行 malloc_device 和 malloc_shared 分配以避免混淆。
OpenMP中分配USM的API
为了与 SYCL USM 模型保持一致,我们添加了三个新的 OpenMP API 作为Intel的 扩展API,供用户根据应用程序、内存大小和性能要求执行内存分配。 它们的语义和性能特征在以下小节。
主机内存分配
这种主机内存比设备内存更容易使用,因为我们不必在主机和设备之间手动复制数据。 主机内存从主机内存中分配,主机和设备均可访问。 这些内存虽然可以在设备上访问,但无法迁移到设备的附加内存。 相反,部署到设备上的kernel从该内存读取或写入该内存的区域是通过 PCIe 总线或结构链路”远程”完成的。 便利性和性能之间的权衡是我们必须考虑的。尽管主机内存可能会产生更高的访问成本, 但仍然有充分的理由使用它们。 比如很少访问的数据或无法容纳在设备附加内存中的大型数据集。 执行主机内存分配的API是
extern void *omp_target_alloc_host(size_t size, int device_num)
设备内存分配
用户需要设备内存这种方式,以便拥有指向设备附加内存的指针,例如设备上的 (G)DDR 或 HBM。 设备内存分配可以通过部署到设备上运行的kernel来读取或写入,但不能从主机上执行的代码直接访问它们。 在主机上尝试访问设备内存可能会导致数据不正确或程序崩溃。 执行设备内存分配的API是
extern void *omp_target_alloc_device(size_t size, int device_num)
共享内存分配
与主机内存一样,共享内存可以在主机和设备上访问。 它们之间的区别在于,共享内存可以自动在主机内存和设备附加内存之间自由迁移, 无需我们干预。 如果内存已迁移到设备,则在该设备上执行的任何kernel访问该设备内存的性能都将比从主机远程访问它的性能更高。 然而,共享分配有很多好处,它也是有缺点的,例如页面迁移成本和乒乓效应
extern void *omp_target_alloc_shared(size_t size, int device_num)
omp_target_alloc
API对USM的支持
用于目标内存分配的OpenMP API是基于 “#pragma omp requires unified shared memory” 实现的,并且使用环境变量 LIBOMPTARGET_USM_HOST_MEM 来支持三种内存分配方式. 其实现逻辑见下面的代码.
1if (exists(“#pragma omp requires unified_shared_memory”)) { 2 if (LIBOMPTARGET_USM_HOST_MEM == 1) 3 return "host memory"; 4 else 5 return "shared memory"; 6} else { 7 return "device memory"; 8}