DAY22:阅读计算模式

3.3. Versioning and Compatibility【版本控制和兼容性】

There are two version numbers that developers should care about when developing a CUDA application: The compute capability that describes the general specifications and features of the compute device (see Compute Capability) and the version of the CUDA driver API that describes the features supported by the driver API and runtime.

The version of the driver API is defined in the driver header file as CUDA_VERSION. It allows developers to check whether their application requires a newer device driver than the one currently installed. This is important, because the driver API is backward compatible, meaning that applications, plug-ins, and libraries (including the C runtime) compiled against a particular version of the driver API will continue to work on subsequent device driver releases as illustrated in Figure 11. The driver API is not forward compatible, which means that applications, plug-ins, and libraries (including the C runtime) compiled against a particular version of the driver API will not work on previous versions of the device driver.

It is important to note that there are limitations on the mixing and matching of versions that is supported:

· Since only one version of the CUDA Driver can be installed at a time on a system, the installed driver must be of the same or higher version than the maximum Driver API version against which any application, plug-ins, or libraries that must run on that system were built.

· All plug-ins and libraries used by an application must use the same version of the CUDA Runtime unless they statically link to the Runtime, in which case multiple versions of the runtime can coexist in the same process space. Note that if nvcc is used to link the application, the static version of the CUDA Runtime library will be used by default, and all CUDA Toolkit libraries are statically linked against the CUDA Runtime.

· All plug-ins and libraries used by an application must use the same version of any libraries that use the runtime (such as cuFFT, cuBLAS, ...) unless statically linking to those libraries.

Figure 11. The Driver API Is Backward but Not Forward Compatible

3.4. Compute Modes

On Tesla solutions running Windows Server 2008 and later or Linux, one can set any device in a system in one of the three following modes using NVIDIA's System Management Interface (nvidia-smi), which is a tool distributed as part of the driver:

· Default compute mode: Multiple host threads can use the device (by calling cudaSetDevice() on this device, when using the runtime API, or by making current a context associated to the device, when using the driver API) at the same time.

· Exclusive-process compute mode: Only one CUDA context may be created on the device across all processes in the system and that context may be current to as many threads as desired within the process that created that context.

· Exclusive-process-and-thread compute mode: Only one CUDA context may be created on the device across all processes in the system and that context may only be current to one thread at a time.

· Prohibited compute mode: No CUDA context can be created on the device.

This means, in particular, that a host thread using the runtime API without explicitly calling cudaSetDevice() might be associated with a device other than device 0 if device 0 turns out to be in the exclusive-process mode and used by another process, or in the exclusive-process-and-thread mode and used by another thread, or in prohibited mode. cudaSetValidDevices() can be used to set a device from a prioritized list of devices.

Note also that, for devices featuring the Pascal architecture onwards (compute capability with major revision number 6 and higher), there exists support for Compute Preemption. This allows compute tasks to be preempted at instruction-level granularity, rather than thread block granularity as in prior Maxwell and Kepler GPU architecture, with the benefit that applications with long-running kernels can be prevented from either monopolizing the system or timing out. However, there will be context switch overheads associated with Compute Preemption, which is automatically enabled on those devices for which support exists. The individual attribute query function cudaDeviceGetAttribute() with the attribute cudaDevAttrComputePreemptionSupported can be used to determine if the device in use supports Compute Preemption. Users wishing to avoid context switch overheads associated with different processes can ensure that only one process is active on the GPU by selecting exclusive-process mode.

Applications may query the compute mode of a device by checking the computeMode device property (see Device Enumeration).

本文备注/经验分享:

Versioning and Compatibility——

这里主要说了:

什么是backword compatible?

这个东西也叫downward compatible,对应的反义词是forward compatible / upword compatible。backward (downword)compatible是向下兼容,也就是兼容低版本。例如这里说的Driver API is backword compatible,就是在高版本的Driver API下,可以兼容以前的老的低版本的Driver API环境。因为环境总是给程序用的,所以用了老版本的Driver API环境的程序,总是被高版本的Driver API所兼容。

(1)这包含直接使用Driver API的应用程序,可以在高版本的Driver API的未来环境下(更新了显卡驱动)运行。 (2)因为Runtime API是构建在Driver API之上的,所以使用了低版本的Runtime所针对的低版本的Driver API的应用,也可以在高版本的Driver API的未来环境下运行。 (3)因为CUDA跟随Runtime自带了一些扩展库(cublas,cusparse,cufft之类的)是针对当前版本的runtime的,而根据(2)runtime已知可以在未来的高版本的Driver API下运行,所以他们也能被未来的高版本的Driver API环境兼容。

所以The Driver API Is Backward Compatible等于是说,在当前的低Driver API版本基础上的所有开发(包括直接的Driver API应用,还是在其之上的Runtime API应用,还是在Runtime API之上的应用(例如只进行cublas计算的应用)),都可以被未来的高版本Driver API保证支持。 这就是为何说Driver API能向下兼容的原因。

但是Driver API不能向上兼容,你不能被设计成一个用高版本的Driver API的应用,编译好了,去要求一台10年前,从来没有更新驱动的老机器,能直接兼容这个高版本的应用。

实际上我们经常看到错误提示是说,驱动版本不够。这个时候我们应当更新驱动。论坛有个例子:

我们的答复:

这个用户感觉自己不能使用高版本的驱动。这种是一种很错误的常见想法的。驱动版本(Driver)可以随时更新到最新的。而开发包可以保持低版本。因为新驱动总是兼容老开发包里的Driver API的接口文件,Runtime API文件,其他补充库文件。这就是刚才说的Driver API是向下兼容的用途。看过手册章节这节的,就不会疑惑能不能随时更新驱动的问题了。

接着说开发包的版本。开发包是死的,从你下载安好的那一刻起,它就是这个版本了。这个版本信息所以说是固定的。可以通过一个常量(cuda.h头文件中的CUDA_VERSION常量)来确定。这也是我们常说的,我们为CUDA XXX开发了应用这里的XXX的含义。第二种则是驱动版本,这个是可变的,但总是兼容以前的低版本下开发的应用(就是刚才说的),因为这个是可变的,动态的,我们可以通过程序,随时通过调用cudaDriverGetVersion()或者cuDriverGetVersion()来获取到当前实际的驱动版本。例如deviceQuery运行的时候,显示的Driver / Runtime: XXX.0 / YYY.0对比,就是从这里来的。如果你运行过deviceQuery的时候看的仔细一点,会发现这点。第三个则是实际的runtime版本。类似的这个可以通过cudaRuntimeGetVersion()来获取。但是因为现在runtime默认总是静态链接(静态链接就是一个库或者runtime变成你的可执行文件的一部分,而不是单独的一个文件。例如一个dll),而静态链接发生在你用当前的开发包开发的时候。所以目前通过这个函数得到的版本,总是和第一个版本相同。而在很早很早之前,runtime默认是动态链接的,是磁盘上单独存在的一个文件(cudart***.dll),当时即使在之前,人们也总是将从开发包中的这个dll跟随exe发布,所以以前动态获取的版本种类3的值,也是等于第一种的静态值的。所以这个函数(cudaRuntimeGetVersion)很大程度上没有用了)。因为总是可以从静态的CUDA_VERSION中获得,值是一样的。 CUDA_VERSION这个常量的格式是XXYY, 例如6.5的CUDA开发包里,这个值是6050, 代表CUDA 6.5 。

请注意这里的6050和CUDA 6.5的对应关系,前者是一个整数,百位以后的是主版本,百位以内的是副版本。没有小数点的。常见的代码中,往往是这个值(CUDA_VERSION)和3020对比。CUDA 3.2版本对Driver和Runtime API接口做了较大的改动。很多代码(特别是老代码)往往需要将这个值和3.2来进行对比。从而选择性的进行一些处理。而CUDA 3.2到今天的CUDA 9.1,软件接口趋于稳定好久了。这是从软件方面说的。硬件上也有一个版本数字,计算能力版本。例如我们常说的计算能力6.1,

计算能力6.0....这个是硬件所支持的特性版本。软件开发包(例如CUDA 9.1)总是包含了它所支持范围的最高硬件特性(计算能力)的卡的所有特性。但是用户需要自行判断实际使用的卡的计算能力,较低的卡的计算能力有些东西不能用的。例如,CUDA开发包,总是提供了动态并行支持,但是实际的硬件的计算能力需要大于等于3.5,才能启用这个特性。否则如果硬件不支持,而硬要使用CUDA软件开发包里的动态并行特性,虽然编译的时候能正常在这个CUDA开发包版本下编译。 但是运行的时候会出错。运行不起来的。所以这是为何说,要自行判断卡的计算能力的原因。不要以为CUDA开发包里的所有特性都能使用,很多需要看卡的。但NV保证了,高版本的计算能力,总是向下兼容低版本的计算能力的所有特性。有的时候为了这个保证,会出现一些奇葩的硬件情况。

之前有人问我普通的家用6.1的卡(Pascal,例如1080Ti), 的half支持情况么? 我当时说,普通的家用的Pascal只有兼容性的半精度支持。你还记得当时我用的词么。这是因为6.0的卡,已经支持half了(GP100,200% half),根据NV的设计理念,6.1的卡必须支持half。 但是实际上,6.0的卡(GP100)是作为训练卡来用的,它有200%的half。而6.1的卡只是用来做推导的,因为它有的只是400%的INT8(6.0的卡没有INT8),但是根据刚才说的,6.1的必须要支持half的,这怎么办?于是NV想出了绝招,6.1的卡的确支持half,但速率只有1/64, 为6.0设计的使用half的程序的确可以在6.1上跑。但是速度极度缓慢....所以实际上,这一代的卡,我们只将6.0作为训练卡来用,而只将6.1作为推理的卡来用(用half训练好用,用tensorrt转换成INT8部署)

Compute Modes——

关于计算模式,这个是专业卡的专有特性。我来简单说一下。一共有4种模式: (1)默认是完全不限制。这个是专业卡的默认模式。也是家用卡的唯一模式。就是说CUDA应用可以随意的使用该卡。你平常见的,我们说的,没有特殊说明的,就是这种模式。

(2)是禁止使用该卡。也就是禁用掉该卡的CUDA能力了。例如一个学校,有用户登录了一个8卡的系统,根据用户的不同,管理员可以设定 成,学生只能使用CPU计算,而当老师登录的时候,可以自动切换成老师可以使用CPU+GPU。这样学生不会黑用烧卡或者用学校资产挖矿,而老师可以充分发挥这台机器的作用,但又不需要人工的每次拆除掉卡,或者安装上卡。注意:专业卡才能这样,普通卡(例如GTX1080Ti)用户只能完全不限制。需要说明的是,Titan系列除外,Titan系列也能。(但是Titan没有ECC啊!!!!一般称为Titan叫做准专业卡。)

(3)将卡只能被1个用户进程使用。对于非计算专业的人来说,你可以理解成1个进程是一个启动的应用。模式3则是进程独占模式,每次只能有1个进程使用该卡。无论你启动多种不同的应用产生的进程,还是1个应用启动多次产生的进程,这些进程里面,只能有1个能用卡的,其他都会运行失败挂掉。这样保证了一个应用的1个进程可以独占一个卡。以便独享该卡的所有性能。不要以为这个是降低了性能,这个设计是有用的,某些情况下,更好的响应性更为关键。能保证性能的稳定,保证性能的独占是很关键的。所以虽然说这个设计貌似降低了总体性能,但既然是专业卡能设计出这个特性,说明还是有人需要的。 类似的,专业卡还能设定禁用Boost,总是以一个较低的频率运行。而家用卡不能禁用掉Boost,随时可以提升性能。专业卡为何要这样设计?有的时候,性能的稳定(总是一个稍微低一点,但是恒定的性能--例如某个处理总是10.1ms完成),好处可能超过了性能可能提升,但不稳定(例如家用卡不能禁用Boost,频率随时可能提升,实际时间轴9.5到10.1之间变动)。 具体原文的说明是,对该设备,系统上的所有进程,只能有一个进程在该卡上创建CUDA Context。这个说法实际上就是等效于刚才的说法(只能有一个应用的1个进程使用该卡)

因为:

  • CUDA必须有CUDA Context才能用卡,所以能否创建context等于能否使用卡(如果你忘记了context是什么,之前你阅读过。往回看)
  • CUDA的Runtime API是隐式初始化+自动管理Context的,而大部分的人总是使用的是Runtime而不是直接Driver API的,这也等效于在一个卡上能否用CUDA。所以这是为何说进程独占模式等于只有1个进程能使用CUDA。而不是原文的解释。(但精确的解释是原文的这种。因为可能有人用CUDA Driver API,需要手工管理context。此时等于原文说法,系统上的1个进程才能为该卡创建1个context)

(4)进程+线程独占模式。

还记得线程是神马吗?一种常见的说法是,为了能充分利用多核心的CPU(或者GPU),你需要多线程。而不能1个线程跑到黑。这个说法很好的描绘了线程的作用----利用CPU或者计算设备的计算能力。但线程的实际定义是,代码流执行的调度实体。(类似的,进程实际上是资源管理的实体)。 你一个CPU进程打开了一个文件,文件句柄是对所有的该进程中的线程可见的。你从一个EXE启动了一个进程,EXE中所包含的数据或者代码(统称资源),是被启动的这个进程所有的。所以说进程是实际上的资源管理的实体。 而线程是执行调度的实体。然后你会有疑惑。我从来没有写过创建线程的代码啊,也从来没用过多线程编程,我就将我的编译出来的exe双击了一下,里面就一个main函数,根据刚才的说法,只是启动了一个进程啊,只是包含一些资源而已,怎么能开始执行下去? 执行的代码流不是线程负责的么?1个进程中总是至少包含1个线程的, 如果你从来没有启动过任何线程,一个CPU上的进程,操作系统将自动为他创建1个线程来从头开始执行的。但是在本手册中,所有的进程都是指的CPU进程,而CUDA进程被总是成为Context。 context等于是GPU上的进程。手册故意不用进程这个词,是为了怕你混淆。

好了。回到刚才的最后一种模式,进程和线程独占模式,这里的进程根据刚才说的,是指host上的进程的。 在这种模式下,该系统上(例如一台GPU服务器),只能同时有1个应用的1个进程中的1个线程,能在同1个时刻使用该卡上的CUDA能力的。如果你不是一个多线程的CPU应用, 此模式将等价于刚才的倒数第二种模式。无区别的。如果你是一个多线程的CPU应用程序,那么该CPU进程中的多个CPU线程, 只能有一个1线程在同一个时刻使用该卡的。类似的,这降低了总体性能。但能保证为一个特定的进程中的特定线程,保证性能的稳定性。 有的时候这个很重要的。专业卡很看重稳定性的,而不是性能。(不过现在大家都黑上深度学习了,跑的越快越好, 所以这个就不注重了。但深度学习不是100%的用户都用的,很多其他领域依然需要稳定性的)。

类似的,这个也是解释后的说法,原文是“该进程中,只有1个线程能将context设定成本线程current的”,这个也涉及到context手工管理的概念(SetCurrent)具体是什么等到了Driver API的时候再说。对于Runtime API用户(大部分用户), 直接理解成只有1个进程中的1个线程能用1个卡的CUDA能力即可。

以上就是 4种模式。其中后三种模式只能专业卡用。

有不明白的地方,请在本文后留言

或者在我们的技术论坛bbs.gpuworld.cn上发帖

原文发布于微信公众号 - 吉浦迅科技(gpusolution)

原文发表时间:2018-05-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CSDN技术头条

解锁Spring Data Redis的正确使用姿势

Redis 是一个开源的使用 ANSIC 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从2010年...

3637
来自专栏Java学习网

网页打开时都发生了什么?我被吓着了

  在浏览器里输入网址或者点击链接,网页打开了……这是我们上网时再普通不过的一幕,但是如此简单的表象背后,却隐藏着无比复杂的技术流程。想涨涨知识吗?往下看吧。 ...

3366
来自专栏我和PYTHON有个约会

30.企业级开发进阶2:网络编程

网络编程部分开始,要求对内容的宏观理解的东西更加多了,简单梳理总结一下,希望大家多提意见一起完善。

500
来自专栏程序员的SOD蜜

“批量少次”还是“少量多次”--邮件通信系统效率浅谈

 在做Web开发的时候,相信很多人都看过一个“批量少次”原则:     Web服务器采用HTTP协议,它是一个非持久连接的协议,是无状态的(虽然可以采用多种...

1895
来自专栏代码GG之家

只需一个命令,快速定位android的启动耗时

有兴趣合作,帮忙制作公众号的一些宣传图册的伙伴,可以加我微信,商谈具体事宜。 回顾: Android 启动过程框架 这节我们讲一个命令,用来定位android...

1756
来自专栏程序员宝库

Golang 大杀器之性能剖析 PProf

想要进行性能优化,首先瞩目在 Go 自身提供的工具链来作为分析依据,本文将带你学习、使用 Go 后花园,涉及如下:

783
来自专栏我和PYTHON有个约会

爬虫正传-江湖路远-0104-狡兔三窟

那是一个繁忙的午后: ?:最近忙什么呢,服务器大佬? ?:别提了,之前收拾了几个不懂事的小游侠,还没有消停几天,压力又上来了! ?:怎么回事?不是捣乱的数...

511
来自专栏杨建荣的学习笔记

一次归档报错的处理和分析(r7笔记第60天)

昨天在睡觉前接到了一条报警短信,本来已经疲倦的身轻如燕,但是看到报警,还是警觉了起来 ZABBIX-监控系统: --------------------...

2914
来自专栏BeJavaGod

我们为何要使用多线程,它有什么优点?

其实在平时的开发中,很多程序员都不会去写线程,为啥?因为麻烦,其次是用到的地方并不多,除非逼不得已,大家都不会去写,毕竟写一天代码,拿一天工资,是吧? ? 麻...

3344
来自专栏SDNLAB

你所了解的三层交换机,是这样工作的

为什么我们说三层交换机的三层转发性能要比路由器的效率要高的多?有时候在很多书里会提及到现在路由器的软件做的也非常强大,几乎也能够达到限速转发的能力;但是软件能够...

3868

扫码关注云+社区