CUDA入门之统一内存

原文来自CUDA 编程入门之统一内存

🎬个人简介:一个全栈工程师的升级之路!
📋个人专栏:高性能(HPC)开发基础教程
🎀CSDN主页 发狂的小花
🌄人生秘诀:学习的本质就是极致重复!

目录

What Unified Memory Delivers

Simpler Programming and Memory Model

Performance Through Data Locality

Unified Memory or Unified Virtual Addressing?

Example: Eliminate Deep Copies

Example: CPU/GPU Shared Linked Lists

Unified Memory with C++

A Bright Future for Unified Memory


借助 CUDA 6,NVIDIA 引入了 CUDA 平台历史上最引人注目的编程模型改进之一,即统一内存。在当今典型的 PC 或集群节点中,CPU 和 GPU 的内存在物理上是不同的,并由 PCI-Express 总线分开。在 CUDA 6 之前,程序员就是这样看待事物的。CPU 和 GPU 之间共享的数据必须在两个内存中分配,并由程序在它们之间显式复制。这给 CUDA 程序增加了很多复杂性。

统一内存创建了一个在 CPU 和 GPU 之间共享的托管内存池,弥合了 CPU-GPU 鸿沟。CPU 和 GPU 都可以使用单个指针访问托管内存。关键是系统会自动在主机和设备之间迁移统一内存中分配的数据。

在这篇文章中,作者将向您展示统一内存如何显著简化 GPU 加速应用程序中的内存管理。下图显示了一个非常简单的示例。两种代码都从磁盘加载文件,对其中的字节进行排序,然后在 CPU 上使用排序后的数据,然后释放内存。右侧的代码使用 CUDA 和统一内存在 GPU 上运行。唯一的区别是 GPU 版本启动内核(并在启动后同步),并使用新的 API 为统一内存中的加载文件分配空间cudaMallocManaged()

如果您以前编写过 CUDA C/C++,那么您无疑会被右侧代码的简洁性所震撼。请注意,我们分配了一次内存,并且我们有一个指向可以从主机和设备访问的数据的指针。我们可以直接从文件中读取分配,然后我们可以直接将指针传递给在设备上运行的 CUDA 内核。然后,等待内核完成后,我们可以再次从 CPU 访问数据。CUDA 运行时隐藏了所有复杂性,自动将数据迁移到访问它的地方。

What Unified Memory Delivers

程序员从统一内存中受益的主要方式有两种。

Simpler Programming and Memory Model

统一内存通过使设备内存管理成为优化而不是要求,降低了在 CUDA 平台上进行并行编程的门槛。借助统一内存,现在程序员可以直接开发并行 CUDA 内核,而不会陷入分配和复制设备内存的细节中。这将使学习为 CUDA 平台编程和将现有代码移植到 GPU 变得更简单。但这不仅适用于初学者。本文后面的示例展示了统一内存如何使复杂的数据结构更容易与设备代码一起使用,以及它与 C++ 结合时的强大功能。

Performance Through Data Locality

通过在 CPU 和 GPU 之间按需迁移数据,Unified Memory 可以在 GPU 上提供本地数据的性能,同时提供全局共享数据的易用性。此功能的复杂性被隐藏在 CUDA 驱动程序和运行时,确保应用程序代码更易于编写。迁移的重点是实现每个处理器的全带宽;250 GB/s 的 GDDR5 内存对于满足 Kepler GPU 的计算吞吐量至关重要。

重要的一点是,使用流并cudaMemcpyAsync有效地与数据传输重叠执行的精心调整的 CUDA 程序可能比仅使用统一内存的 CUDA 程序表现得更好。可以理解的是:CUDA 运行时从来没有像程序员那样拥有关于何时何地需要数据的信息!CUDA 程序员仍然可以访问显式设备内存分配和异步内存副本,以优化数据管理和 CPU-GPU 并发性。统一内存首先是一种生产力功能,它为并行计算提供了更平滑的入口,而不会剥夺 CUDA 为高级用户提供的任何功能。

Unified Memory or Unified Virtual Addressing?

CUDA 自 CUDA 4 起就支持统一虚拟寻址 (UVA),虽然统一内存依赖于 UVA,但它们并不是一回事。UVA 为系统中的所有内存提供一个单一的虚拟内存 地址空间 ,并允许从 GPU 代码访问指针,无论它们位于系统中的哪个位置,无论是设备内存(在相同或不同 GPU 上)、主机内存、或片上共享内存。它还允许cudaMemcpy在不指定输入和输出参数的确切位置的情况下使用。UVA 启用“零拷贝”内存,它是固定的主机内存,可通过 PCI-Express 直接由设备代码访问,无需memcpy. 零拷贝提供了统一内存的一些便利,但没有提供性能,因为它总是以 PCI-Express 的低带宽和高延迟访问。

UVA 不会像统一内存那样自动将数据从一个物理位置迁移到另一个物理位置。因为统一内存能够在主机和设备内存之间的单个页面级别自动迁移数据,所以它需要大量的工程来构建,因为它需要 CUDA 运行时、设备驱动程序甚至操作系统内核中的新功能。以下示例旨在让您了解这可以实现什么。

Example: Eliminate Deep Copies

统一内存的一个关键优势是通过在访问 GPU 内核中的结构化数据时消除对深拷贝的需求来简化异构计算内存模型。将包含指针的数据结构从 CPU 传递到 GPU 需要执行“深度复制”,如下图所示。

以下面的 struct 为例dataElem

struct dataElem {
  int prop1;
  int prop2;
  char *name;
}

要在设备上使用这个结构,我们必须复制结构本身及其数据成员,然后复制结构指向的所有数据,然后更新结构副本中的所有指针。这导致以下复杂代码,只是为了将数据元素传递给内核函数。

void launch(dataElem *elem) {
  dataElem *d_elem;
  char *d_name;

  int namelen = strlen(elem->name) + 1;

  // Allocate storage for struct and name
  cudaMalloc(&d_elem, sizeof(dataElem));
  cudaMalloc(&d_name, namelen);

  // Copy up each piece separately, including new “name” pointer value
  cudaMemcpy(d_elem, elem, sizeof(dataElem), cudaMemcpyHostToDevice);
  cudaMemcpy(d_name, elem->name, namelen, cudaMemcpyHostToDevice);
  cudaMemcpy(&(d_elem->name), &d_name, sizeof(char*), cudaMemcpyHostToDevice);

  // Finally we can launch our kernel, but CPU & GPU use different copies of “elem”
  Kernel<<< ... >>>(d_elem);
}

可以想象,在 CPU 和 GPU 代码之间共享复杂数据结构所需的额外主机端代码对生产力有重大影响。在统一内存中分配我们的dataElem结构消除了所有多余的设置代码,只剩下内核启动,它在与主机代码相同的指针上运行。这是一个很大的进步!

void launch(dataElem *elem) {
  kernel<<< ... >>>(elem);
}

但这不仅仅是代码复杂性的重大改进。统一记忆还使以前无法想象的事情成为可能。让我们看另一个例子。

Example: CPU/GPU Shared Linked Lists

链表是一种非常常见的数据结构,但由于它们本质上是由指针组成的嵌套数据结构,因此在内存空间之间传递它们是非常复杂的。如果没有统一内存,在 CPU 和 GPU 之间共享一个链表是无法管理的。唯一的选择是在零拷贝内存(固定主机内存)中分配列表,这意味着 GPU 访问仅限于 PCI-express 性能。通过在统一内存中分配链表数据,设备代码可以在 GPU 上正常跟随指针,充分发挥设备内存的性能。该程序可以维护单个链表,并且可以从主机或设备中添加和删除列表元素。

将具有现有复杂数据结构的代码移植到 GPU 曾经是一项艰巨的任务,但统一内存使这变得容易得多。作者希望统一内存能够为 CUDA 程序员带来巨大的生产力提升。

Unified Memory with C++

统一内存确实在 C++ 数据结构中大放异彩。C++ 通过使用带有复制构造函数的类来简化深复制问题。复制构造函数是一个知道如何创建类的对象、为其成员分配空间以及从另一个对象复制其值的函数。C++ 还允许重载 new 和 delete 内存管理操作符。这意味着我们可以创建一个基类,我们将其称为 Managed,它在重载的 new 运算符中使用 cudaMallocManaged(),如下面的代码所示。

class Managed {
public:
  void *operator new(size_t len) {
    void *ptr;
    cudaMallocManaged(&ptr, len);
    cudaDeviceSynchronize();
    return ptr;
  }

  void operator delete(void *ptr) {
    cudaDeviceSynchronize();
    cudaFree(ptr);
  }
};

然后我们可以让我们的String类从该类继承Managed,并实现一个复制构造函数,为复制的字符串分配统一内存。

// Deriving from “Managed” allows pass-by-reference
class String : public Managed {
  int length;
  char *data;

public:
  // Unified memory copy constructor allows pass-by-value
  String (const String &s) {
    length = s.length;
    cudaMallocManaged(&data, length);
    memcpy(data, s.data, length);
  }

  // ...
};

同样,我们让我们的dataElem类继承Managed

// Note “managed” on this class, too.
// C++ now handles our deep copies
class dataElem : public Managed {
public:
  int prop1;
  int prop2;
  String name;
};

通过这些更改,C++ 类在统一内存中分配它们的存储,并且自动处理深拷贝。我们可以像任何 C++ 对象一样在统一内存中分配一个 dataElem。

dataElem *data = new dataElem;

请注意,您需要确保树中的每个类都继承自Managed,否则您的内存映射中有一个漏洞。实际上,您可能需要在 CPU 和 GPU 之间共享的所有内容都应该继承Managed. 如果您更喜欢简单地对所有内容使用统一内存,则可以在全局范围内重载newdelete但这仅在您没有纯 CPU 数据时才有意义,否则数据将不必要地迁移。

现在,当我们将对象传递给内核函数时,我们有一个选择;在 C++ 中很正常,我们可以按值传递或按引用传递,如下面的示例代码所示。

// Pass-by-reference version
__global__ void kernel_by_ref(dataElem &data) { ... }

// Pass-by-value version
__global__ void kernel_by_val(dataElem data) { ... }

int main(void) {
  dataElem *data = new dataElem;
  ...
  // pass data to kernel by reference
  kernel_by_ref<<<1,1>>>(*data);

  // pass data to kernel by value -- this will create a copy
  kernel_by_val<<<1,1>>>(*data);
}

多亏了统一内存,深拷贝、按值传递和按引用传递都可以正常工作。这为在 GPU 上运行 C++ 代码提供了巨大的价值。

这篇文章中的示例可在 Github 上找到。

A Bright Future for Unified Memory

CUDA 6 中统一内存最令人兴奋的事情之一是它只是一个开始。他们围绕统一内存​​计划了一个很长的改进和功能路线图。他们的第一个版本旨在使 CUDA 编程更容易,特别是对于初学者。从 CUDA 6 开始,cudaMemcpy()不再需要。通过使用cudaMallocManaged(),您有一个指向数据的指针,并且您可以在 CPU 和 GPU 之间共享复杂的 C/C++ 数据结构。这使得编写 CUDA 程序变得更加容易,因为您可以直接编写内核,而不是编写大量数据管理代码并维护所有数据的重复主机和设备副本。您仍然可以自由使用cudaMemcpy()(尤其是 cudaMemcpyAsync())性能,但它现在不是一种要求,而是一种优化。

CUDA 的未来版本可能会通过添加数据预取和迁移提示来提高使用统一内存的应用程序的性能。他们还将增加对更多操作系统的支持。他们的下一代 GPU 架构将带来多项硬件改进,以进一步提高性能和灵活性。

🌈我的分享也就到此结束啦🌈
如果我的分享也能对你有帮助,那就太好了!
若有不足,还请大家多多指正,我们一起学习交流!
📢未来的富豪们:点赞👍→收藏⭐→关注🔍,如果能评论下就太惊喜了!
感谢大家的观看和支持!最后,☺祝愿大家每天有钱赚!!!欢迎关注、关注!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/450743.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

X64 页表结构

PML4&#xff08;Page Map Level 4&#xff09;是x86-64架构中用于管理虚拟内存地址翻译的四级页表结构之一。它是一种树形结构&#xff0c;由多个页目录表&#xff08;Page Directory Pointer Table&#xff0c;PDPT&#xff09;组成&#xff0c;每个PDPT有512个指向下一级页表…

低功耗DC-DC电压调整器IU5528D

IU5528D是一款超微小型,超低功耗,高效率,升降压一体DC-DC调整器。适用于双节,三节干电池或者单节锂电池的应用场景。可以有效的延长电池的使用时间。IU5528D由电流模PWM控制环路&#xff0c;误差放大器&#xff0c;比较器和功率开关等模块组成。该芯片可在较宽负载范围内高效稳…

1_springboot_shiro_jwt_多端认证鉴权_Shiro入门

1. Shiro简介 Shiro 是 Java 的一个安全框架&#xff0c;它相对比较简单。主要特性&#xff1a; Authentication&#xff08;认证&#xff09;&#xff1a;用户身份识别&#xff0c;通常被称为用户“登录”&#xff0c;即 “你是谁”Authorization&#xff08;授权&#xff…

webpack-dev-server5.0+ 版本问题

webpack-dev-server版本选择 在使用webpack-dev-server搭建新项目时&#xff0c;需要依赖node 和webpack以及webpack-cli 这是需要注意各个应用之间的版本问题 通过npm官网查看webpack-dev-server使用的版本依赖对象 先看package.json&#xff0c;可以看到当前的版本 再找到依…

YOLOv9使用训练好的权重检测目标

打开yolov9-main\detect.py文件 1修改为训练后权重文件的位置 2改为要检测图片的位置 3修改成数据集的yaml文件 运行detect.py文件并解决报错 打开报错文件yolov9-main\utils\general.py,在prediction = prediction[0]后边加上[0] 继续运行detect.py,成功检测 存在问题 当…

9个免费游戏后端平台

在这篇文章中&#xff0c;您将看到 九个免费的游戏服务平台提供商&#xff0c;这可以帮助您开始在线多人游戏&#xff0c;而无需预先投入大量资金。 每个提供商都有非常独特的功能&#xff0c;因此成本应该只是决定时要考虑的方面之一。 我还从低预算项目的角度对免费提供商进…

如何在Ubuntu系统部署DbGate数据库管理工具并结合cpolar内网穿透远程访问

文章目录 1. 安装Docker2. 使用Docker拉取DbGate镜像3. 创建并启动DbGate容器4. 本地连接测试5. 公网远程访问本地DbGate容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 本文主要介绍如何在Linux Ubuntu系统中使用Docker部署DbGate数据库管理工…

WordPress供求插件API文档:获取市场类型

请注意&#xff0c;该文档为&#xff1a; WordPress供求插件&#xff1a;一款专注于同城生活信息发布的插件-CSDN博客文章浏览阅读396次&#xff0c;点赞6次&#xff0c;收藏5次。WordPress供求插件&#xff1a;sliver-urban-life 是一款专注于提供同城生活信息发布与查看的插件…

CSS伪类与常用标签属性整理与块级、行级、行级块标签(文本,背景,列表,透明,display)

目录 文本 color&#xff1a;字体颜色 font-size&#xff1a;字体大小​编辑 front-family&#xff1a;字体 text-align&#xff1a;文本对齐 text-decoration:line-through&#xff1a;定义穿过文本下的一条线 text-decoration:underline&#xff1a;定义文本下的一条线…

mysqld.exe运行时,提示缺少msvcr100.dll,msvcp100.dll文件,导致mysql安装失败或mysql服务无法启动

mysqld.exe运行时&#xff0c;提示缺少msvcr100.dll&#xff0c;msvcp100.dll文件&#xff0c;导致mysql安装失败或无法启动 msvcr100.dll&#xff0c;msvcp100.dll时VC2010的动态链接库。 1、下载地址 https://www.microsoft.com/zh-cn/download/details.aspx?id26999&wd…

027—pandas 不同分类每天指定取值的比例

前言 本例我们将进行分组计算&#xff0c;分组后得到一个堆叠数据&#xff0c;并对堆叠数据解除堆叠&#xff0c;最后再按要求格式化为百分数样式。 此类操作会经常发生在业务数据透视场景下&#xff0c;一般都会有 Excel 来操作完成&#xff0c;今天我们使用 Python 的 panda…

钉钉扫码登录,sdk问题

别问我为啥会写这玩意。因为有人问到了 1.钉钉扫码登录&#xff0c;网上代码一大堆&#xff0c;但是小同学在抄的时候突然问我&#xff0c;为啥jar包倒入不了。pom添加了&#xff0c;镜像也是阿里的&#xff0c;还是不行 下载了包&#xff0c;按这个放啊发去操作就好了 1.先…

1.Datax数据同步之Windows下,mysql数据同步至另一个mysql数据库

目录 前言步骤操作大纲步骤明细其他问题 前言 Datax是什么&#xff1f; DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台&#xff0c;实现包括 MySQL、SQL Server、Oracle、PostgreSQL、HDFS、Hive、HBase、OTS、ODPS 等各种异构数据源之间高效的数据同步功能。准备…

Hand 3D相关

看到一个不错的文献总结网址&#xff0c;如下 GitHub - SeanChenxy/Hand3DResearch 涉及的内容如下图&#xff1a;

基于SSM框架的民族文化传承与乡村扶贫网站设计与实现【附项目源码】分享

民族文化传承与乡村扶贫网站设计与开发: 源码地址&#xff1a;https://download.csdn.net/download/qq_41810183/88842794 一、引言 随着信息技术的飞速发展&#xff0c;互联网已成为文化传播与经济发展的重要平台。为了有效传承和弘扬民族文化&#xff0c;同时推动乡村地区…

【Linux】Shell编程【一】

shell是一个用 C 语言编写的程序&#xff0c;它是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言。 Shell 是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问操作系统内核的服务。 Shell属于内置的…

Docker上部署LPG(loki+promtail+grafana)踩坑复盘

Docker上部署LPG&#xff08;lokipromtailgrafana&#xff09;踩坑复盘 声明网上配置部署踩坑多机采集 声明 参考掘金文章&#xff1a;https://juejin.cn/post/7008424451704356872 版本高的用docker compose命令&#xff0c;版本低的用docker-compose 按照文章描述&#xff0c…

应急响应-Webshell-典型处置案例

网站后台登录页面被篡改 事件背景 在2018年11月29日4时47分&#xff0c;某网站管理员发现网站后台登录页面被篡改&#xff0c;“中招”服务器为windows系统&#xff0c;应采用java语言开发&#xff0c;所使用的中间件为Tomcat。 事件处置 Webshell排查 利用D盾对网站目录进…

C++_包装器

目录 1、包装器的用法 2、包装器的类型 3、包装器的作用 4、包装成员函数 5、bind&#xff08;绑定&#xff09; 5.1 bind的用法 5.2 bind减少参数个数 结语 前言&#xff1a; C11的包装器&#xff0c;总称为function包装器&#xff0c;而包装器又称适配器…

模块化机房的成本效益分析

在当今日益数字化的商业环境中&#xff0c;数据中心的建设和运营成本成为企业关注的重点。模块化机房以其独特的优势&#xff0c;不仅满足了快速部署和高效能的需求&#xff0c;还显著降低了总体成本。本文将深入探讨模块化机房在不同方面带来的成本效益。 1.预制模块化设计 …