GPU是一个外围设备,本来是专门作为图形渲染使用的,但是随着其功能的越来越强大,GPU也逐渐成为继CPU之后的又一计算核心。但不同于CPU的架构设计,GPU的架构从一开始就更倾向于图形渲染和大规模数据的并行计算处理。而大规模的并行计算,离不开大规模的数据传输,只有深入了解了GPU的存储体系,才能真正发挥GPU的威力,写出高性能的软件产品。但是由于GPU存储体系相关的资料非常少,加之非常分散,所以在看了大量的零散资料后,想通过这篇文章,总结一下关于GPU存储相关的知识点,以期达到加深理解的目的。

GPU存储体系的设计哲学是更大的内存带宽,而不是更低的访问延迟。该设计原则不同于CPU依赖多级Cache来降低内存访问延迟的策略,GPU则是通过大量的并行线程来规避或者叫隐藏内存访问的延迟,具体来说就是GPU在等待某个内存数据到来的时候,会运行成百上千个其他与该数据无关的线程,来处理另外的数据请求,这就是GPU存储体系内存访问的特点:高带宽,高延迟。

正式开始之前,我们需要了解几个基础的概念。我们通常在讲内存时,多数情况下都是指CPU的专用存储,从GPU存储的角度来说,CPU的内存一般称之为主存(main memory),GPU自己的存储则称为local memory,即GPU的本地存储,有时候也称为video memory。

GPU的存储体系根据GPU的类型不同,可以是逻辑上的,也可以是物理上的。对于集成显卡(即integrated GPU)而言,例如 Intel HD Graphics ,GPU和CPU位于同一die中,所以它没有自己的物理存储设备,而是共享CPU的存储空间,即Unified Memory Architecture(一致性存储架构),通常是从CPU的存储中划分一部分出来作为该GPU的local memory;另一种显卡称为独立显卡(即dedicated GPU),像Nvidia和AMD生产的GPU就属于这类,它们都拥有自己的物理存储设备,是我们日常使用最多的GPU类型了。无论哪种GPU,它都拥有自己的一套地址空间,且独立于CPU的虚拟内存地址空间。GPU地址空间的管理是通过内核态驱动来完成的,例如Windows上的KMD(Kernel-Mode Driver)

对于integrated gpu而言,因为GPU和CPU处于同一die中,所以GPU和CPU很多时候是共享总线的。除了GPU自己的local memory之外,CPU和GPU之间有时候需要共享一些数据,例如在渲染的时候,CPU将顶点数据放入主存当中,供GPU使用。由于主存的内容对GPU来说是不可见的,所以GPU是不能直接访问这些数据的。为了让GPU访问CPU主存的内容,业界引入了一个叫GART(即Graphic Address Remapping Table)的技术。GART是一个 I/O memory management unit (IOMMU) ,说白了就是一个内存地址的映射表,可以将CPU的内存地址重新映射到GPU的地址空间,这样就可以让显卡直接访问(DMA,direct memory access)host system memory。

反过来,CPU如何访问GPU的存储空间呢?因为integrated gpu的存储空间是从主存分出的一部分,一般情况下都比较小,OS可以将GPU的整个存储空间映射到CPU的地址空间。但是对于dedicated gpu来说,这种方式就不行了,因为独立显卡的显存一般比较大,一个32位的OS整个地址空间也才4GB。所以独立显卡拥有与integrated gpu不同的地址空间映射机制,用于解决这个问题。一种比较常用的方式是映射一部分GPU存储空间到CPU的地址空间,典型大小为256MB/512MB,这段地址空间会通过PCIe的bar获取一个CPU可见的地址空间。最新的PCIe支持 resize bar技术,支持该技术的GPU可以动态调整映射区域的大小。

简单介绍完GPU的存储体系后,我以OpenGL程序为例,来分析一下OpenGL中数据的upload和download过程,从而了解GPU存储体系在实际程序中的运用。

OpenGL更新数据的常用函数家族是:glBuffer*Data和glMapBuffer*。更准确的说是CPU需要更新数据给GPU使用时,顶点数据的更新,纹理数据的上传等等,需要CPU到GPU的数据传输,这个过程称为streaming。这个数据传输的过程有两种方式:

l  glBufferData/glBufferSubData

通过这两个函数,可以将数据从main memory拷贝到pinned memory,一旦拷贝完成,就会发起一次异步的DMA(Direct Memory Access)传输,将数据传输给GPU,然后就会从函数调用返回,一旦函数返回,你就可以对原来CPU主存中的数据做任何处理,修改或者删除。

l  glMap*/glUnmap*

通过mapping的方式传输数据,你可以获取一个指向pinned memory的指针,通过该指针你可以拷贝main memory上的数据到pinned memory,然后调用glUnmap通知driver你已经完成数据的更新,这种方式看似跟上面的glBufferData/glBufferSubData一样,但是你可以获得更多的控制权。

该例子中的pinned memory就是CPU内存上的一块专门用于GART的存储区域,DMA传输则是通过上文提到的PCIe bar来实现的,了解了GPU的存储体系,在使用图形API进行渲染绘制时,才能清晰的了解数据的归属和流向,从而避免不必要的错误和性能损失。

参考链接:

https://www.makeuseof.com/tag/can-shared-graphics-finally-compete-with-a-dedicated-graphics-card/

https://lwn.net/Articles/257417/

https://zhuanlan.zhihu.com/p/35891701

https://fgiesen.wordpress.com/2011/07/01/a-trip-through-the-graphics-pipeline-2011-part-1/

GPU体系架构(二):GPU存储体系的更多相关文章

  1. 深入GPU硬件架构及运行机制

    目录 一.导言 1.1 为何要了解GPU? 1.2 内容要点 1.3 带着问题阅读 二.GPU概述 2.1 GPU是什么? 2.2 GPU历史 2.2.1 NV GPU发展史 2.2.2 NV GPU ...

  2. android体系架构

    android体系架构总结: android体系架构分为四层 第一层:应用层:applications 第二层:开发层 第三层:

  3. hbase的存储体系

    一.了解hbase的存储体系. hbase的存储体系核心的有Split机制,Flush机制和Compact机制. 1.split机制 每一个hbase的table表在刚刚开始的时候,只有一个regio ...

  4. NUMA 体系架构

    NUMA 体系架构 SMP 体系架构 NUMA 体系架构 NUMA 结构基本概念 Openstack flavor NUMA 策略 Nova 实现 NUMA 流程 1. SMP 体系架构 CPU 计算 ...

  5. 2020再见&新的计划(建立Android体系架构)

    2020,再见 关于2020,我心中有四个关键词: 疫情 年初突如其来的疫情,打破了原本生活的节奏,也没想到会笼罩全世界整整一年,希望这个世界早点好起来吧. 科比 初三的早晨,噩耗传来,我一度不敢相信 ...

  6. GPU体系架构(一):数据的并行处理

    最近在了解GPU架构这方面的内容,由于资料零零散散,所以准备写两篇博客整理一下.GPU的架构复杂无比,这两篇文章也是从宏观的层面去一窥GPU的工作原理罢了 GPU根据厂商的不同,显卡型号的不同,GPU ...

  7. InnoDB体系架构(二)内存

    InnoDB体系架构(二)内存 上篇文章 InnoDB体系架构(一)后台线程 介绍了MySQL InnoDB存储引擎后台线程:Master Thread.IO Thread.Purge Thread. ...

  8. MySQL InnoDB存储引擎体系架构 —— 索引高级

    转载地址:https://mp.weixin.qq.com/s/HNnzAgUtBoDhhJpsA0fjKQ 世界上只两件东西能震撼人们的心灵:一件是我们心中崇高的道德标准:另一件是我们头顶上灿烂的星 ...

  9. F2工作流引擎这工作流引擎体系架构(二)

    F2工作流体系架构概览图 为了能更好的了解F2工作流引擎的架构体系,花了些时间画了整个架构的体系图.F2工作流引擎遵循参考WFCM规范,目标是实现轻量级的工作流引擎,支持多种数据库及快速应用到任何基于 ...

随机推荐

  1. 龙芯 fedora28 安装指南

    版权声明:原创文章,未经博主允许不得转载 关于硬件 龙芯3号的板子安装系统都差不多,我分别在 Lemote A1310 和 Lemote A1901 上都尝试过. 本文主要依据 Lemote A190 ...

  2. larabel Artisan Command 使用总结

    larabel Artisan Command 使用总结 定义命令 在routes/console.php下定义命令 Artisan::command('ltf', function () { (ne ...

  3. Git详解之服务部署

    前言 到目前为止,你应该已经学会了使用 Git 来完成日常工作.然而,如果想与他人合作,还需要一个远程的 Git 仓库.尽管技术上可以从个人的仓库里推送和拉取修改内容,但我们不鼓励这样做,因为一不留心 ...

  4. Prometheus学习笔记之教程推荐

    最近学习K8S和基于容器的监控,发现了如下的教程质量不错,记录下来以备参考 K8S最佳实战(包括了K8S的Prometheus监控和EFK日志搜集) https://jimmysong.io/kube ...

  5. 关于PreparedStatement.addBatch()方法

    Statement和PreparedStatement的区别就不多废话了,直接说PreparedStatement最重要的addbatch()结构的使用. 1.建立链接,(打电话拨号 ) Connec ...

  6. 搭建一个V 2ray的方法

    VPS构建VPN教程 (由于博客限制有些敏感词 V 2ray中间会打空格或者(删掉我)图片中的敏感词进行了马赛克处理) 关于自建VPN翻墙教程,此处是利用V 2 ray的一个VPS搭建VPN教程.便于 ...

  7. GitHub注册与Git安装

    注册GitHub GitHub官网 ☞☞☞☞[戳我] 一.打开,如下图所示, 按照要求进行注册. 安装Git Git官网下载☞☞☞☞ [戳我] 一.打开,根据自己的电脑系统选择不同的版本,这里主要是以 ...

  8. Codeforces_732_D

    http://codeforces.com/problemset/problem/732/D 二分查找. #include<iostream> #include<cstring> ...

  9. HDU3836 Equivalent Sets (Tarjan缩点+贪心)

    题意: 给你一张有向图,问你最少加多少条边这张图强连通 思路: 缩点之后,如果不为1个点,答案为出度为0与入度为0的点的数量的最大值 代码: #include<iostream> #inc ...

  10. (七)mybatis批量操作,分页插件

    首先使用方式很简单: SqlSession sqlSession = sessionFactory.openSession(ExecutorType.BATCH); 批量操作核心:改变执行sql的方式 ...