为什么使用 cudaMallocManaged 时 NVIDIA Pascal GPU 运行 CUDA 内核的速度很慢

2023-12-09

我正在测试新的 CUDA 8 以及 Pascal Titan X GPU,并期望我的代码能够加速,但由于某种原因,它最终变得更慢。我使用的是 Ubuntu 16.04。

这是可以重现结果的最少代码:

CUDASample.cuh

class CUDASample{
 public:
  void AddOneToVector(std::vector<int> &in);
};

CUDASample.cu

__global__ static void CUDAKernelAddOneToVector(int *data)
{
  const int x  = blockIdx.x * blockDim.x + threadIdx.x;
  const int y  = blockIdx.y * blockDim.y + threadIdx.y;
  const int mx = gridDim.x * blockDim.x;

  data[y * mx + x] = data[y * mx + x] + 1.0f;
}

void CUDASample::AddOneToVector(std::vector<int> &in){
  int *data;
  cudaMallocManaged(reinterpret_cast<void **>(&data),
                    in.size() * sizeof(int),
                    cudaMemAttachGlobal);

  for (std::size_t i = 0; i < in.size(); i++){
    data[i] = in.at(i);
  }

  dim3 blks(in.size()/(16*32),1);
  dim3 threads(32, 16);

  CUDAKernelAddOneToVector<<<blks, threads>>>(data);

  cudaDeviceSynchronize();

  for (std::size_t i = 0; i < in.size(); i++){
    in.at(i) = data[i];
  }

  cudaFree(data);
}

Main.cpp

std::vector<int> v;

for (int i = 0; i < 8192000; i++){
  v.push_back(i);
}

CUDASample cudasample;

cudasample.AddOneToVector(v);

唯一的区别是 NVCC 标志,对于 Pascal Titan X 来说是:

-gencode arch=compute_61,code=sm_61-std=c++11;

对于旧的 Maxwell Titan X 来说是:

-gencode arch=compute_52,code=sm_52-std=c++11;

编辑:以下是运行 NVIDIA Visual Profiling 的结果。

For the old Maxwell Titan, the time for memory transfer is around 205 ms, and the kernel launch is around 268 us. enter image description here

For the Pascal Titan, the time for memory transfer is around 202 ms, and the kernel launch is around an insanely long 8343 us, which makes me believe something is wrong. enter image description here

我通过将 cudaMallocManaged 替换为旧的 cudaMalloc 来进一步隔离问题,并进行了一些分析并观察了一些有趣的结果。

CUDASample.cu

__global__ static void CUDAKernelAddOneToVector(int *data)
{
  const int x  = blockIdx.x * blockDim.x + threadIdx.x;
  const int y  = blockIdx.y * blockDim.y + threadIdx.y;
  const int mx = gridDim.x * blockDim.x;

  data[y * mx + x] = data[y * mx + x] + 1.0f;
}

void CUDASample::AddOneToVector(std::vector<int> &in){
  int *data;
  cudaMalloc(reinterpret_cast<void **>(&data), in.size() * sizeof(int));
  cudaMemcpy(reinterpret_cast<void*>(data),reinterpret_cast<void*>(in.data()), 
             in.size() * sizeof(int), cudaMemcpyHostToDevice);

  dim3 blks(in.size()/(16*32),1);
  dim3 threads(32, 16);

  CUDAKernelAddOneToVector<<<blks, threads>>>(data);

  cudaDeviceSynchronize();

  cudaMemcpy(reinterpret_cast<void*>(in.data()),reinterpret_cast<void*>(data), 
             in.size() * sizeof(int), cudaMemcpyDeviceToHost);

  cudaFree(data);
}

For the old Maxwell Titan, the time for memory transfer is around 5 ms both ways, and the kernel launch is around 264 us. enter image description here

For the Pascal Titan, the time for memory transfer is around 5 ms both ways, and the kernel launch is around 194 us, which actually results in the performance increase I am hoping to see... enter image description here

为什么使用 cudaMallocManaged 时 Pascal GPU 运行 CUDA 内核的速度如此之慢?如果我必须将所有使用 cudaMallocManaged 的​​现有代码恢复为 cudaMalloc,那将是一种讽刺。这个实验还表明,使用cudaMallocManaged的内存传输时间比使用cudaMalloc慢很多,这也感觉有些不对劲。如果使用此方法会导致运行时间变慢,即使代码更简单,这应该是不可接受的,因为使用 CUDA 而不是普通 C++ 的全部目的是为了加快速度。我做错了什么以及为什么我会观察到这种结果?


在带有 Pascal GPU 的 CUDA 8 下,统一内存 (UM) 机制下的托管内存数据迁移通常会与以前的架构不同,您正在体验这种影响。 (另请参阅末尾有关 Windows 的 CUDA 9 更新行为的注释。)

对于以前的架构(例如 Maxwell),特定内核调用使用的托管分配将在内核启动时立即全部迁移,大约就像您调用cudaMemcpy自己移动数据。

使用 CUDA 8 和 Pascal GPU,数据迁移通过请求分页进行。在内核启动时,默认情况下,没有数据显式迁移到设备(*)。当GPU设备代码尝试访问未驻留在GPU内存中的特定页面中的数据时,将会发生页面错误。此页面错误的最终影响是:

  1. 导致 GPU 内核代码(访问该页面的一个或多个线程)停止(直到步骤 2 完成)
  2. 导致该内存页从CPU迁移到GPU

当 GPU 代码涉及各个数据页时,将根据需要重复此过程。上述步骤 2 中涉及的操作顺序涉及一些latency除了实际移动数据所花费的时间之外,还需要处理页面错误。由于此过程一次将数据移动一页,因此它可能比一次移动所有数据的效率低得多,或者使用cudaMemcpy或者通过 Pascal 之前的 UM 安排,导致所有数据在内核启动时移动(无论是否需要,也无论内核代码何时实际需要它)。

两种方法都有其优点和缺点,我不想争论优点或不同的意见或观点。请求调页过程为 Pascal GPU 提供了许多重要的特性和功能。

然而,这个特定的代码示例并没有受益。这是预料之中的,因此建议使用在内核启动之前使用cudaMemPrefetchAsync() call.

您可以使用 CUDA 流语义强制此调用在内核启动之前完成(如果内核启动未指定流,您可以为流参数传递 NULL,以选择默认流)。我相信这个函数调用的其他参数是不言自明的。

通过在内核调用之前调用此函数来覆盖有问题的数据,您不应在 Pascal 情况下观察到任何页面错误,并且配置文件行为应与 Maxwell 情况类似。

正如我在评论中提到的,如果您创建了一个依次涉及两个内核调用的测试用例,您会发现即使在 Pascal 情况下,第二个调用也几乎全速运行,因为所有数据都已迁移通过第一次内核执行到 GPU 端。因此,这个预取功能的使用不应该被认为是强制的或自动的,而应该被深思熟虑地使用。在某些情况下,GPU 可能能够在某种程度上隐藏页面错误的延迟,并且显然不需要预取已经驻留在 GPU 上的数据。

请注意,上述步骤 1 中提到的“停顿”可能会产生误导。内存访问本身不会触发停顿。但是,如果操作确实需要请求的数据,例如乘法,那么扭曲将在乘法操作处停止,直到必要的数据可用。那么,一个相关点是,如果有足够的其他可用“工作”需要参与,以这种方式从主机到设备的数据请求分页只是 GPU 可能隐藏在其延迟隐藏架构中的另一种“延迟”到。

作为补充说明,在 CUDA 9 中,pascal 及更高版本的请求分页机制仅在 Linux 上可用;之前在 CUDA 8 中宣传的对 Windows 的支持已被放弃。看here。在 Windows 上,即使对于 Pascal 设备及更高版本,从 CUDA 9 开始,UM 机制与 maxwell 和之前的设备相同;数据在内核启动时全部迁移到 GPU。

(*) 这里的假设是,在托管分配调用之后,数据“驻留在”主机上,即已经在 CPU 代码中“触及”或初始化。托管分配本身会创建与设备关联的数据页,当 CPU 代码“接触”这些页面时,CUDA 运行时将要求对驻留在主机内存中的必要页面进行分页,以便 CPU 可以使用它们。如果您执行分配但从未“接触”CPU 代码中的数据(可能是一种奇怪的情况),那么当内核运行时它实际上已经“驻留在”设备内存中,并且观察到的行为将会不同。但对于这个特定的例子/问题来说,情况并非如此。

更多信息可在this博客文章。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么使用 cudaMallocManaged 时 NVIDIA Pascal GPU 运行 CUDA 内核的速度很慢 的相关文章

  • 将复选框添加到 UniformGrid

    我正在尝试将复选框动态添加到 wpf 中的统一网格中 但看起来网格没有为它们分配足够的空间 所以它们都有点互相重叠 这就是我将它们添加到后面的代码中的方法 foreach string folder in subfolders PathCh
  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 如何检查图像对象与资源中的图像对象是否相同?

    所以我试图创建一个简单的程序 只需在单击图片框中更改图片即可 我目前只使用两张图片 所以我的图片框单击事件函数的代码 看起来像这样 private void pictureBox1 Click object sender EventArgs
  • 是否可以强制 XMLWriter 将元素写入单引号中?

    这是我的代码 var ptFirstName tboxFirstName Text writer WriteAttributeString first ptFirstName 请注意 即使我使用 ptFirstName 也会以双引号结束 p
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • Newtonsoft JSON PreserveReferences处理自定义等于用法

    我目前在使用 Newtonsoft Json 时遇到一些问题 我想要的很简单 将要序列化的对象与所有属性和子属性进行比较以确保相等 我现在尝试创建自己的 EqualityComparer 但它仅与父对象的属性进行比较 另外 我尝试编写自己的
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • 将自定义元数据添加到 jpeg 文件

    我正在开发一个图像处理项目 C 我需要在处理完成后将自定义元数据写入 jpeg 文件 我怎样才能做到这一点 有没有可用的图书馆可以做到这一点 如果您正在谈论 EXIF 元数据 您可能需要查看exiv2 http www exiv2 org
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • 当操作繁忙时,表单不执行任何操作(冻结)

    我有一个使用 C 的 WinForms 应用程序 我尝试从文件中读取一些数据并将其插入数据表中 当此操作很忙时 我的表单冻结并且无法移动它 有谁知道我该如何解决这个问题 这可能是因为您在 UI 线程上执行了操作 将文件和数据库操作移至另一个
  • 如何在 VBA 中声明接受 XlfOper (LPXLOPER) 类型参数的函数?

    我在之前的回答里发现了问题 https stackoverflow com q 19325258 159684一种无需注册即可调用 C xll 中定义的函数的方法 我之前使用 XLW 提供的注册基础结构 并且使用 XlfOper 类型在 V
  • 将 unsigned char * (uint8_t *) 转换为 const char *

    我有一个带有 uint8 t 参数的函数 uint8 t ihex decode uint8 t in size t len uint8 t out uint8 t i hn ln for i 0 i lt len i 2 hn in i
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • C - 直接从键盘缓冲区读取

    这是C语言中的一个问题 如何直接读取键盘缓冲区中的数据 我想直接访问数据并将其存储在变量中 变量应该是什么数据类型 我需要它用于我们研究所目前正在开发的操作系统 它被称为 ICS OS 我不太清楚具体细节 它在 x86 32 位机器上运行
  • 为什么我收到“找不到编译动态表达式所需的一种或多种类型。”?

    我有一个已更新的项目 NET 3 5 MVC v2 到 NET 4 0 MVC v3 当我尝试使用或设置时编译出现错误 ViewBag Title财产 找不到编译动态表达式所需的一种或多种类型 您是否缺少对 Microsoft CSharp
  • Validation.ErrorTemplate 的 Wpf 动态资源查找

    在我的 App xaml 中 我定义了一个资源Validation ErrorTemplate 这取决于动态BorderBrush资源 我打算定义独特的BorderBrush在我拥有的每个窗口以及窗口内的不同块内
  • x86 上未对齐的指针

    有人可以提供一个示例 将指针从一种类型转换为另一种类型由于未对齐而失败吗 在评论中这个答案 https stackoverflow com questions 544928 reading integer size bytes from a
  • 如何在 C++ BOOST 中像图形一样加载 TIFF 图像

    我想要加载一个 tiff 图像 带有带有浮点值的像素的 GEOTIFF 例如 boost C 中的图形 我是 C 的新手 我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能 Boost GIL load tiif

随机推荐

  • 右键单击 jqGrid 时禁用行选择

    在 jqGrid 中 我当前使用以下命令禁用行选择 beforeSelectRow function return false 这对于左键单击效果很好 但是 我注意到它没有触发beforeSelectRow事件处理程序 并且当我右键单击时仍
  • CKeditor保存事件

    我按照本主题中写的步骤进行操作 CKEditor AJAX 保存如果有人按下 AjaxSave 按钮 我尝试触发自定义 saved ckeditor 事件 但我没有成功 ckeditor plugins ajaxsave plugin js
  • 等待 IE 文件下载完成的 VBA 代码

    我正在尝试从网页下载 Excel 文件 到目前为止 我能够打开网页 导航并单击 保存 按钮 但下载后我需要访问该 Excel 文件 但有时下载需要时间 具体取决于文件的大小 有什么方法可以检查窗口并查看下载是否完成 然后才能继续打开下载的文
  • 如何删除 BigQuery 中属于嵌套列的列

    我想删除 BigQuery 表中属于记录或嵌套列的列 我在他们的中找到了这个命令文档 不幸的是 此命令不适用于现有 RECORD 字段内的嵌套列 有什么解决方法吗 例如 如果我有这个架构 我想删除地址字段内的 address2 字段 所以由
  • 拦截 ESC 而不从缓冲区中删除其他按键

    我有一个控制台应用程序 提示用户进行多个输入 我希望用户能够在出现任何取消操作的提示后按转义键 就像是 if Console ReadKey Key ConsoleKey Escape string input Console ReadLi
  • 时间:2019-03-17 标签:c#updatepanelwithtimerpage_load

    我现在正在尝试一些 AJAX 我有一个自定义控件出现在我的母版页上 其中有一个更新面板和一个计时器 计时器启动 面板更新 一切都很顺利 除了我不希望它在每次刷新时执行一些操作之外 似乎每次刷新都会发生整个页面生命周期 我想设置一些变量 并在
  • 指数维护

    什么是索引维护以及如何进行 我需要多久做一次 有什么好处 这与经常修改的事务表有关 所有 DML 操作都将在该表上运行 我赞同乔纳森所说的一切 除了索引维护的频率 好吧 如果您碰巧有一个设计不佳的索引 例如 GUID 键上的聚集索引 您实际
  • 将 CURDATE() 的日期值与完整时间戳字段进行比较

    我有一个函数将时间戳值 YYYY MM DD HH MM SS 放入META VALUE表的列META 我想要做的是比较日期部分 YYYY MM DD 是否META VALUE等于今天 CURDATE 忽略小时 分钟和秒 HH MM SS
  • 尝试访问 Rails 控制台时 git 远程中的多个应用程序

    我有两个 git 分支 staging and production 我将它们部署在 Heroku 上的同一个 Heroku 帐户中 假设我的应用程序名称是app1 heroku app com and app2 heroku app co
  • 如何使用 ggplot2 在 R 中添加可变大小的 y 轴标签而不更改绘图宽度?

    我有一个用 R 中的 ggplot2 制作的图 我想在 y 轴上添加水平文本标签 然而 根据文本的长度 R 会相应地压缩我的绘图以创建固定宽度的图像 但是 无论文本宽度如何 我都需要绘图具有相同的长度并且具有相同的起始位置和停止位置 边距
  • Selenium:会话外部密钥不可用

    每当 Robot Framework 自动化测试 由 Jenkins 作业启动 从 Hub 请求 Chrome 浏览器时 我正在运行的 Selenium Grid Hub 就会显示此错误消息 会话 null externalkey 不可用且
  • Selenium:是否有类似“DOM 中插入新元素”之类的事件

    我正在测试的网站有一个通知逻辑 它会在屏幕底部显示一条消息 将其保留一秒钟 然后将其发送出去 当显示通知时 它会隐藏其他元素 这使我的测试不稳定 我尽力弄清楚通知何时显示 当业务逻辑显示通知时 并忽略它 但时不时地我会检测到我的代码不知道通
  • 如何将项目添加到列表

    我的项目中有模型 这是模型代码 public partial class Logging public string Imei get set public DateTime CurDateTime get set public Nulla
  • 在 Outlook 中自动调整 VML 背景图像的大小

    我知道关于防弹电子邮件背景hack 但由于这会在背景中放置一个设定大小的 VML 矩形 然后将内容放置在其中 因此它不会调整大小 或者换句话说 表格单元格中的文本被裁剪为 VML 矩形的高度 我已经尝试了我能想到的一切 但似乎无论如何都不允
  • 创建新实体时不会自动生成相对路由

    当我使用命令 jhipster实体 entityName 创建新实体时 相对路径不会在我的 entityName route ts中自动生成 相反 在主路线的地方我有这个 而不是 实体名称 另外 所有添加 更新 删除的路由也不包含前缀 en
  • AChartEngine不显示最大图表值

    我正在尝试使用 AChartEngine 显示水平条形图 在条形图中 应显示 ChartValues 我在代码中使用 XYSeriesRenderer setDisplayChartValues true 以下是渲染的图表 正如您所看到的
  • 使用 QTcpSocket 的 TCP 数据包

    我知道 TCP 保证所有数据包都会到达 但是一个数据包可以分成2个或更多吗 我正在使用 Qt 和 QTcpSocket 类 我想知道的是ReadyRead 仅当完整数据包到达时才会发出信号 或者换句话说 以第一个字节发送数据包大小 然后循环
  • 来自资源包的值作为 formatDate 中的模式

    我也想从资源包中读取 JST formatDate 的模式 但这种天真的方法不起作用 我做错了什么 在 com company MyPortlet properties 中是这个键 company date format yyyy MM d
  • 在 UICollectionView 或 UITableView 中实现粘性单元格

    我要实现一个包含项目列表的表格 其中包括一个应始终显示在屏幕上的项目 因此 例如 您的列表中有 50 项 您的 粘性 列表项是第 25 个 您可能会同时在屏幕上显示 10 个项目 无论您在列表中的位置如何 粘性 列表应始终保持可见 如果您的
  • 为什么使用 cudaMallocManaged 时 NVIDIA Pascal GPU 运行 CUDA 内核的速度很慢

    我正在测试新的 CUDA 8 以及 Pascal Titan X GPU 并期望我的代码能够加速 但由于某种原因 它最终变得更慢 我使用的是 Ubuntu 16 04 这是可以重现结果的最少代码 CUDASample cuh class C