【知识】pytorch中的pinned memory和pageable memory

news2024/11/15 9:30:27

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn]

如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~

目录

概念简介

pytorch用法

速度测试

反直觉情况


概念简介

        默认情况下,主机 (CPU) 数据分配是可分页的。GPU 无法直接从可分页主机内存访问数据,因此当调用从可分页主机内存到设备内存的数据传输时,CUDA 驱动程序必须首先分配一个临时的页面锁定或“固定”主机数组,将主机数据复制到固定数组,然后将数据从固定阵列传输到设备内存。 

        如图所示,固定内存用作从设备到主机的传输暂存区域。通过直接在固定内存中分配主机阵列,我们可以避免在可分页主机阵列和固定主机阵列之间传输的成本。使用 cudaMallocHost() 或 cudaHostAlloc() 在 CUDA C/C++ 中分配固定主机内存,并使用 cudaFreeHost() 解除分配。固定内存分配可能会失败,因此应始终检查错误。

        数据传输速率可能取决于主机系统的类型(主板、CPU 和芯片组)以及 GPU。通过运行BandwidthTest会产生以下结果。可见,固定传输的速度是可分页传输的两倍多。(我的测试发现,基本上能跑满PCIe的带宽。

Device: NVS 4200M
Transfer size (MB): 16

Pageable transfers
  Host to Device bandwidth (GB/s): 2.308439
  Device to Host bandwidth (GB/s): 2.316220

Pinned transfers
  Host to Device bandwidth (GB/s): 5.774224
  Device to Host bandwidth (GB/s): 5.958834

        不过,不应过度分配固定内存。这样做会降低整体系统性能,因为它会减少操作系统和其他程序可用的物理内存量。多少是太多是很难提前判断出来的,因此与所有优化一样,测试你的应用程序及其运行的系统以获得最佳性能参数。

用法示例

        由于pinned memory后,可以使用DMA传输而不占用CPU,因此通常需要搭配non_blocking使用。

# tensor.pin_memory() 就行
pinned_tensor = torch.randn(data_size, dtype=torch.float32).pin_memory()

device = torch.device("cuda")
pinned_tensor.to(device, non_blocking=True)

速度测试

import torch
import time
import torch.multiprocessing as mp

# 数据大小
data_size = 10**7  # 例如,10M数据

def test_pinned_memory(rank, normal_tensor, pinned_tensor, device):
    # 测试普通内存到GPU传输时间
    start_time = time.perf_counter()
    normal_tensor_gpu = normal_tensor.to(device, non_blocking=True)
    torch.cuda.synchronize()  # 等待数据传输完成
    normal_memory_time = time.perf_counter() - start_time
    print(f"[进程 {rank}] 普通内存到GPU传输时间: {normal_memory_time:.6f} 秒")

    # 测试固定内存到GPU传输时间
    start_time = time.perf_counter()
    pinned_tensor_gpu = pinned_tensor.to(device, non_blocking=True)
    torch.cuda.synchronize()  # 等待数据传输完成
    pinned_memory_time = time.perf_counter() - start_time
    print(f"[进程 {rank}] 固定内存到GPU传输时间: {pinned_memory_time:.6f} 秒")

    # 比较结果
    speedup = normal_memory_time / pinned_memory_time
    print(f"[进程 {rank}] 固定内存的传输速度是普通内存的 {speedup:.2f} 倍")

if __name__ == '__main__':
    # 分配普通内存中的张量
    normal_tensor = torch.randn(data_size, dtype=torch.float32)

    # 分配固定内存中的张量
    pinned_tensor = torch.randn(data_size, dtype=torch.float32).pin_memory()

    # 目标设备
    device = torch.device("cuda")

    # 使用mp.spawn启动多进程测试
    mp.spawn(test_pinned_memory, args=(normal_tensor, pinned_tensor, device), nprocs=2, join=True)

输出:

[进程 0] 普通内存到GPU传输时间: 1.054590 秒
[进程 0] 固定内存到GPU传输时间: 0.012945 秒
[进程 0] 固定内存的传输速度是普通内存的 81.47 倍
[进程 1] 普通内存到GPU传输时间: 1.169124 秒
[进程 1] 固定内存到GPU传输时间: 0.013019 秒
[进程 1] 固定内存的传输速度是普通内存的 89.80 倍

可以看到速度还是非常快的。

反直觉情况

        我再瞎试的过程中发现,如果将pinned memory放在一个class中,那么多进程时候,pinned memory的移动很慢。暂不清楚为什么。

        示例代码(反例,仅供观看,请勿使用):

import torch
import torch.multiprocessing as mp
class PinnedMemoryManager:
    def __init__(self, data_size):
        self.data_size = data_size
        self.normal_tensor = None
        self.pinned_tensor = None

    def allocate_normal_memory(self):
        # 分配普通内存
        self.normal_tensor = torch.randn(self.data_size, dtype=torch.float32)

    def allocate_pinned_memory(self):
        # 分配固定内存
        self.pinned_tensor = torch.randn(self.data_size, dtype=torch.float32).pin_memory()

    def transfer_to_device(self, device, use_pinned_memory=False):
        # 选择使用普通内存或固定内存
        tensor = self.pinned_tensor if use_pinned_memory else self.normal_tensor
        if tensor is None:
            raise ValueError("Tensor not allocated. Call allocate_memory first.")

        # 数据传输
        start_time = torch.cuda.Event(enable_timing=True)
        end_time = torch.cuda.Event(enable_timing=True)

        start_time.record()
        tensor_gpu = tensor.to(device, non_blocking=True)
        end_time.record()

        # 同步并计算传输时间
        torch.cuda.synchronize()
        transfer_time = start_time.elapsed_time(end_time) / 1000.0  # 转换为秒
        return tensor_gpu, transfer_time

    def free_memory(self):
        # 释放内存
        del self.normal_tensor
        del self.pinned_tensor
        self.normal_tensor = None
        self.pinned_tensor = None


def test_pinned_memory(rank, manager, device):
    # 测试普通内存到GPU传输时间
    normal_gpu, normal_memory_time = manager.transfer_to_device(device, use_pinned_memory=False)
    print(f"[进程 {rank}] 普通内存到GPU传输时间: {normal_memory_time:.6f} 秒")

    # 测试固定内存到GPU传输时间
    pinned_gpu, pinned_memory_time = manager.transfer_to_device(device, use_pinned_memory=True)
    print(f"[进程 {rank}] 固定内存到GPU传输时间: {pinned_memory_time:.6f} 秒")

    # 比较结果
    speedup = normal_memory_time / pinned_memory_time
    print(f"[进程 {rank}] 固定内存的传输速度是普通内存的 {speedup:.2f} 倍")


if __name__ == '__main__':
    # 数据大小
    data_size = 10**7  # 例如,10M数据

    # 初始化固定内存管理器
    manager = PinnedMemoryManager(data_size)
    manager.allocate_normal_memory()
    manager.allocate_pinned_memory()

    # 目标设备
    device = torch.device("cuda")

    # 使用mp.spawn启动多进程测试
    mp.spawn(test_pinned_memory, args=(manager, device), nprocs=2, join=True)

    # 释放内存
    manager.free_memory()

输出:

[进程 1] 普通内存到GPU传输时间: 0.013695 秒
[进程 1] 固定内存到GPU传输时间: 0.013505 秒
[进程 1] 固定内存的传输速度是普通内存的 1.01 倍
[进程 0] 普通内存到GPU传输时间: 0.013752 秒
[进程 0] 固定内存到GPU传输时间: 0.013593 秒
[进程 0] 固定内存的传输速度是普通内存的 1.01 倍

可以看到基本上没啥改进。

暂不清楚原因,只能先无脑避免这种用法了。

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

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

相关文章

计算机系统的基本结构-CSP初赛知识点整理

真题练习 [2021-CSP-J-第3题] 目前主流的计算机储存数据最终都是转换成( )数据进行储存。 A.二进制 B.十进制 C.八进制 D.十六进制 [2020-CSP-J-第1题] 在内存储器中每个存储单元都被赋予一个唯一的序号,称为( ) A.地址 B&a…

探索 Electron 应用的本地存储:SQLite3 与 Knex.js 的协同工作

electron 简介 Electron 是一个使用 JavaScript, HTML 和 CSS 构建跨平台桌面应用程序的框架。 它允许开发者使用 Web 技术来创建桌面软件,而不需要学习特定于平台的编程语言。 Electron 应用程序实际上是一个包含 Web 内容的 Chromium 浏览器实例,并…

创建型模式(Creational Patterns)之工厂模式(Factory Pattern)之简单工厂模式(Simple Factory Pattern)

1.简单工厂模式(Simple Factory Pattern),又叫做静态工厂方法(Static FactoryMethod Pattern)。 1.1 基本介绍 被创建的对象称为“产品”,创建产品的对象称为“工厂”。如果要创建的产品不多,只…

WPF-实现多语言的静态(需重启)与动态切换(不用重启)

一、多语言切换&#xff08;需重启&#xff09; 1、配置文件添加Key <appSettings><add key"language" value"zh-CN"/></appSettings> 2、新增附加属性当前选择语言 public CultureInfo SelectLanguage{get > (CultureInfo)GetValu…

使用Go语言绘制柱状图教程

使用Go语言绘制柱状图教程 本文将介绍如何使用Go语言及gg包绘制柱状图&#xff0c;并将图表保存为PNG格式的图片。gg包是一个功能强大的2D图形库&#xff0c;适合用于绘制各种图表。 安装gg包 首先&#xff0c;确保你已经安装了gg包。如果还没有安装&#xff0c;可以使用以下…

Java二十三种设计模式-组合模式(11/23)

组合模式&#xff1a;构建层次化结构的灵活方案 引言 组合模式&#xff08;Composite Pattern&#xff09;是一种结构型设计模式&#xff0c;用于将对象组合成树形结构以表示“部分-整体”的层次结构。这种模式使得用户对单个对象和组合对象的使用具有一致性。 基础知识&…

Linux 命令,mkdir说明与使用

1&#xff1a;mkdir命令功用&#xff1a; 用于创建一个或多个目录&#xff0c;创建目录&#xff0c;必须在父目录中写上权限。 新目录的默认模式为0777&#xff0c;可以由系统或用的umask来修改。 2&#xff1a;命令构件: mkdir [options] directories 3:参数选项: -m&#x…

海洋知识竞赛规则流程方案

为贯彻落实“进一步关心海洋、认识海洋、经略海洋”的重要指示精神&#xff0c;引导社会公众学习海洋知识、增强海洋意识、保护海洋环境&#xff0c;推动建设海洋强国&#xff0c;推进人与自然和谐共生的现代化&#xff0c;围绕“保护海洋 人与自然和谐共生”的主题&#xff0c…

机械学习—零基础学习日志(高数22——泰勒公式理解深化)

核心思想&#xff1a;函数逼近 在泰勒的年代&#xff0c;如果想算出e的0.001次方&#xff0c;这是很难计算的。那为了能计算这样的数字&#xff0c;可以尝试逼近的思想。 但是函数又不能所有地方都相等&#xff0c;那退而求其次&#xff0c;只要在一个极小的范围&#xff0c;…

EMQX服务器安装MQTT测试

cd /usr/local/develop wget https://www.emqx.com/en/downloads/broker/5.7.1/emqx-5.7.1-el7-amd64.tar.gz mkdir -p emqx && tar -zxvf emqx-5.7.1-el7-amd64.tar.gz -C emqx ./emqx/bin/emqx start 重启 ./emqx/bin/emqx restart http://10.8.0.1:18083/ 账号ad…

sql第一次

第五关 然后修改userLess-5 Double Query- Single Quotes- Stringhttp://localhost/sql/Less-5/?id1%27%20and%20updatexml(1,concat(0x7e,(select%20group_concat(username,0x3a,password)from%20users),0x7e),1)--substr截取 在前面截取 注意不要少写括号&#xff0c;不然会…

FFmpeg推流

目录 一. 环境准备 二. 安装FFmpeg 三. 给docker主机安装docker服务 四. 使用 FFmpeg 进行推流测试 FFmpeg是一个非常强大的多媒体处理工具&#xff0c;它可以用于视频和音频的录制、转换以及流处理。在流处理方面&#xff0c;FFmpeg可以用来推流&#xff0c;即将本地媒体…

Spring快速学习

目录 IOC控制反转 引言 IOC案例 Bean的作用范围 Bean的实例化 bean生命周期 DI 依赖注入 setter注入 构造器注入 自动装配 自动装配的方式 注意事项; 集合注入 核心容器 容器的创建方式 Bean的三种获取方式 Bean和依赖注入相关总结 IOC/DI注解开发 注解开发…

探索四川财谷通抖音小店:安全与信赖的购物新体验

在数字经济蓬勃发展的今天&#xff0c;抖音平台凭借其庞大的用户基础和强大的内容生态&#xff0c;逐渐成为了电商领域的一股不可忽视的力量。其中&#xff0c;四川财谷通抖音小店作为这一浪潮中的佼佼者&#xff0c;不仅以其丰富的商品种类和独特的品牌魅力吸引了众多消费者的…

【数据结构】排序 —— 快速排序(quickSort)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构、LeetCode专栏 &#x1f4da;本系…

Python酷库之旅-第三方库Pandas(067)

目录 一、用法精讲 266、pandas.Series.dt.second属性 266-1、语法 266-2、参数 266-3、功能 266-4、返回值 266-5、说明 266-6、用法 266-6-1、数据准备 266-6-2、代码示例 266-6-3、结果输出 267、pandas.Series.dt.microsecond属性 267-1、语法 267-2、参数 …

集合基础知识及练习

import java.util.ArrayList;public class Solution {//将字符串转化为整数public static void main(String[] args) {ArrayList<String> listnew ArrayList();list.add("aaa");list.add("aaa");list.add("bbb");list.add("ccc"…

【Python】Django Web 框架

一、常用的Web开发框架 1.Django Django是一个由Python写成的开放源代码的Web应用框架。这套框架的主要目标是使开发复杂、数据库驱动的网站变得简单。Django注重组件的重用性和“可拔插性”、敏捷开发和DRY(Dont Repeat Yourself)法则 2.Flask Flask是一个微型的Python开发…

音视频开发 sdl库

介绍 SDL (Simple DirectMedia Layer) 是一个跨平台的开源多媒体库,它提供了底层访问多种硬件的接口,如音频、视频、输入设备等。它主要用于游戏开发,但也可用于其他类型的多媒体应用程序。下面是 SDL 的一些主要特点: 跨平台性: SDL 支持多种操作系统,包括 Windows、macOS、L…

如何在linux系统上安装tomcat应用程序?

1&#xff09;首先查看安装包信息 yum info tomcat yum info tomcat 2&#xff09;安装 yum -y install tomcat yum -y install tomcat 3&#xff09;查看安装是否成功 rpm -q tomcat rpm -q tomcat 4&#xff09;如果输出一下内容则代表安装成功 tomcat-7.0.76-16.el7_9.n…