CUDA入门之统一内存

news2024/12/23 8:59:46

原文来自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 架构将带来多项硬件改进,以进一步提高性能和灵活性。

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java——正则表达式详解

目录 Java正则表达式1、正则表达式语法1.1、基本的元字符1.2、数量元字符1.3、位置元字符1.4、特殊字符元字符1.5、回溯引用和前后查找1.6、大小写转换1.7、匹配模式 2、Java中的正则表达式2.1、概述2.2、获取匹配位置2.3、捕获组 3、匹配单个字符3.1、匹配纯文本3.2、匹配任意…

小程序对于人力资源行业的创新与变革

随着移动互联网的快速发展&#xff0c;小程序成为了各行各业推广和服务的新利器。对于人力资源行业来说&#xff0c;开发一款定制化的小程序不仅可以提升服务效率&#xff0c;还可以增强品牌形象和用户粘性。那么&#xff0c;如何定制开发人力资源类的小程序呢&#xff1f;下面…

嵌入式3-11

1、整理思维导图 2、尝试学生结构体数组&#xff0c;完成成员的输入&#xff0c;以成绩为条件完成对学生成员的冒泡排序并输出&#xff08;全部在主函数内完成&#xff09; #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.…

【算法】Hash存储——开放寻址法

模拟散列表 维护一个集合&#xff0c;支持如下几种操作&#xff1a; I x&#xff0c;插入一个整数 x&#xff1b; Q x&#xff0c;询问整数 x是否在集合中出现过&#xff1b; 现在要进行 N次操作&#xff0c;对于每个询问操作输出对应的结果。 输入格式 第一行包含整数 N&am…

Retrofit

1.导入依赖 //Retrofit 核心库implementation("com.squareup.retrofit2:retrofit:2.9.0")//响应数据自动序列化//JSONimplementation("com.squareup.retrofit2:converter-gson:2.9.0")//String类型implementation("com.squareup.retrofit2:converter…

Python办公自动化之PDF(二)

Python操作PDF二 1、PyMuPDF简介2、 1、PyMuPDF简介 PyMuPDF&#xff08;也称Fitz&#xff09;开源&#xff0c;提供了一整套用于处理PDF文件的综合工具。使用PyMuPDF&#xff0c;用户可以高效地执行打开PDF、提取文本、图像和表格、操作旋转和裁剪等页面属性、创建新PDF文档以…

详解7道经典指针运算笔试题!

目录 ​编辑 1. 题目一 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 2. 题目二 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 3. 题目三 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;分析 4. 题目四 &#xff08;…

JimuReport积木报表 v1.7.2 版本发布,低代码报表工具

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

C++中类模板的定义和使用

类模板的定义和使用 引言类模板声明和定义有问有答 示例运行结果注意参数传递ref 引言 类模板就是一个模板&#xff0c;但是数据可以适用多种类型。类模板使用时需要模板的特例化&#xff0c;就变成了模板类。 本文只要是记录一下模板的使用。同时对于引用和右值引用传参做一下…

C语言基础练习——Day05

目录 选择题 编程题 数字在升序数组中出现的次数 整数转换 选择题 1、如下程序的功能是 #include <stdio.h> int main() {char ch[80] "123abcdEFG*&";int j;puts(ch);for(j 0; ch[j] ! \0; j){if(ch[j] > A && ch[j] < Z)ch[j] ch[j] e…

【考研数学】张宇学习包

张宇的授课侧重于启发学生的综合思维能力。对于基础较好的学生而言&#xff0c;在听完他的课后&#xff0c;解题通常不会构成太大问题&#xff0c;而且可以学到许多解题技巧&#xff0c;其中包括张宇老师创造的易记的“点火公式”。 然而&#xff0c;对于基础较薄弱的学生来说…

STM32CubeIDE基础学习-STM32CubeIDE软件偏好设置

STM32CubeIDE基础学习-STM32CubeIDE软件偏好设置 文章目录 STM32CubeIDE基础学习-STM32CubeIDE软件偏好设置前言第1章 设置字体颜色第2章 设置字体大小第3章 设置代码区背景颜色总结 前言 编程软件环境最好就设置一个自己喜欢的界面进行显示&#xff0c;这样看起来会比较舒服些…

【fastllm】学习框架,本地运行,速度还可以,可以成功运行chatglm2模型

1&#xff0c;关于 fastllm 项目 https://www.bilibili.com/video/BV1fx421k7Mz/?vd_source4b290247452adda4e56d84b659b0c8a2 【fastllm】学习框架&#xff0c;本地运行&#xff0c;速度还可以&#xff0c;可以成功运行chatglm2模型 https://github.com/ztxz16/fastllm &am…

【你也能从零基础学会网站开发】Web建站之javascript入门篇 Array数组

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 数组概述 …

Vue+SpringBoot打造农家乐订餐系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户2.2 管理员 三、系统展示四、核心代码4.1 查询菜品类型4.2 查询菜品4.3 加购菜品4.4 新增菜品收藏4.5 新增菜品留言 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的农家乐订餐系统&#xff0c…

RabbitMQ 安装登陆 提示:User can only log in via localhost

RabbitMQ默认提供了一个guest/guest用户&#xff0c;但是从3.3.0 版本以后该账号只能localhost登陆&#xff08;User can only log in via localhost&#xff09; 解决办法&#xff1a;通过命令行创建一个管理员账号 通过以下命令创建一个用户user&#xff0c;密码为user12345…

LVGL在VScode中安装模拟器运行配置笔记教程

1、LVGL模拟器工程搭建 LVGL(Light and Versatile Graphics Library,轻巧而多功能的图形库)是一个免费的开放源代码图形库,它提供创建具有易于使用的图形元素,精美的视觉效果和低内存占用的嵌入式GUI所需的一切。本文主要讲述如何实现在VScode中实现LVGL模拟器环境的搭建运行。…

开源是什么?——跟老吕学Python编程

开源是什么&#xff1f;——跟老吕学Python编程 开源是什么&#xff1f;开放源代码软件是什么&#xff1f;开源软件许可证是什么&#xff1f;开放源代码软件是什么&#xff1f;开放源代码的软件代表有什么&#xff1f;开放源代码软件与自由软件的概念 开源的定义是什么&#xf…

【Linux】第四十二站:线程局部存储与线程分离

一、线程的局部存储 1.实现多线程 如果我们想创建多线程&#xff0c;我们可以用下面的代码类似去实现 #include <iostream> #include <pthread.h> #include <string> #include <cstdlib> #include <unistd.h> #include <thread> #inclu…

AI智能答题系统是什么?

AI智能答题系统是一种基于人工智能技术的智能问答系统&#xff0c;旨在提供精准、高效的答题解答服务。该系统利用自然语言处理&#xff08;NLP&#xff09;、机器学习、知识图谱等多种技术&#xff0c;可以理解用户提出的问题&#xff0c;并在庞大的知识库或数据集中查找相关信…