首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >仅在分配的一部分上使用cudaHostRegister安全吗?

仅在分配的一部分上使用cudaHostRegister安全吗?
EN

Stack Overflow用户
提问于 2021-10-08 15:26:45
回答 2查看 565关注 0票数 3

我有一个C++类容器,它分配普通对象的1GB内存(例如内置内存)。

我需要将部分对象复制到GPU。为了加速和简化传输,我想将CPU内存注册为不可分页(“固定”),例如在复制之前使用cudaHostRegister(void*, size, ...)

(这似乎是用最小逻辑复制内存进一步子集的好方法。例如,如果简单的cudaMemcpy是不够的。)

是否安全地传递指向原始分配内存的 part 的指针,例如,原始1GB.的一个连续的100 1GB子集。

我可能只想注册部分是因为效率,但也是因为在调用跟踪的深处,我可能丢失了原始分配指针的信息。

换句话说,指向cudaHostRegister的指针参数可以是分配的指针以外的其他东西吗?特别是从分配的内存导出的算术结果,但仍在分配的范围内。

它似乎有效,但我不明白,一般来说,“固定”分配的一部分是否会以某种方式破坏分配块。

更新:我担心的是分配实际上是在documentation for the cudaHostRegister flag options中提到的

  • cudaHostRegisterDefault:在一个具有统一虚拟寻址的系统上,内存将被映射和移植。在没有统一虚拟寻址的系统上,内存既不能映射也不能移植。

  • cudaHostRegisterPortable:此调用返回的内存将被所有CUDA上下文视为固定内存,而不仅仅是执行allocation.

的内存。

  • cudaHostRegisterMapped:将分配映射到CUDA地址空间。可以通过调用cudaHostGetDevicePointer().

获得指向内存的设备指针。

  • cudaHostRegisterIoMemory:传递的内存指针被视为指向某些内存映射的I/O空间,例如属于第三方PCIe设备,并且它将被标记为非缓存一致和连续的。

  • cudaHostRegisterReadOnly:传递的内存指针被视为指向设备认为只读的内存。在没有cudaDevAttrPageableMemoryAccessUsesHostPageTables,的平台上,为了将映射到CPU的内存注册为只读,需要此标志。可以从设备属性cudaDeviceAttrReadOnlyHostRegisterSupported查询对使用此标志的支持。使用此标志与与没有此属性集的设备关联的当前上下文将导致cudaErrorNotSupported.

中的cudaHostRegister出错。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-10-09 10:00:49

是否可以安全地传递一个指针,该指针仅指向原始分配内存的一部分,例如,原始1GB的一个相邻的100 1GB子集。

虽然我同意文件可能更清楚,但我认为问题的答案是“是”。

原因如下:另一种解释是,只有由malloc返回的整个内存段才允许注册。但是,这是不可行的,因为malloc可以在幕后分配一个大的部分,并且只分配给用户部分。因此,即使您(用户)在cudaHostRegister中使用malloc返回的那些部分,它们实际上也是以前分配的更大内存块的片段。

顺便说一句,Linux有一个类似的内核调用来锁定名为mlock的内存。它接受任意内存范围。

其他答案之一声称(直到这一测试发布):

如果您只需要将对象的一部分复制到GPU,那么使用

()是没有用的,因为它很可能会将数据从物理上复制到其他地方,这样就不会保存任何东西了。

但这是不正确的:注册是值得的,如果要复制的内存块足够大,即使只复制一次。我看到这个代码的速度提高了大约2倍(注释掉了所指示的行),或者如果在计时器之间也做了注销的话,大约是50%。

代码语言:javascript
运行
复制
#include <chrono>
#include <iostream>
#include <vector>
#include <cuda_runtime_api.h>

int main()
{
    std::size_t giga = 1024*1024*1024;
    std::vector<char> src(giga, 3);
    char* dst = 0;
    if(cudaMalloc((void**)&dst, giga)) return 1;

    cudaDeviceSynchronize();
    auto t0 = std::chrono::system_clock::now();

    if(cudaHostRegister(src.data() + src.size()/2, giga/8, cudaHostRegisterDefault)) return 1; // comment out this line
    if(cudaMemcpy(dst, src.data() + src.size()/2, giga/8, cudaMemcpyHostToDevice)) return 1;

    cudaDeviceSynchronize();
    auto t1 = std::chrono::system_clock::now();
    
    auto d = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
    std::cout << (d / 1e6) << " seconds" << std::endl;
    // un-register and free
}
票数 0
EN

Stack Overflow用户

发布于 2021-10-09 09:30:17

这是一个经验之谈,而不是一个恰当的答案:

当数据自动化系统的文档不能保证某件东西能正常工作时,你就需要假设它不起作用。因为如果它确实有效--对你来说,现在,在你所拥有的系统上--它可能会在将来停止工作;或者在另一个系统上,或者在另一个使用场景中。

更具体地说,内存钉扎是在页面分辨率上进行的,所以除非您想要的部分开始并结束在物理页面边界上,否则CUDA驱动程序将需要在您所要求的区域之前和之后插入更多的内存--这是它可以做到的,但它需要多走一英里才能满足您的需要,我怀疑如果没有文档,这种情况是否会发生。

我还建议您通过developer.nvidia.com提交错误报告,要求它们在文档中澄清这一点。我的经验是.大约有50%的几率他们会对这样的错误报告做些什么。

最后-你可以试一试:编写一个程序,复制到GPU与和不固定的部分区域,看看是否有吞吐量的差异。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69498286

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档