DAY52:阅读scheduling

我们正带领大家开始阅读英文的《CUDA C Programming Guide》,今天是第52天,我们正在讲解CUDA C语法,希望在接下来的48天里,您可以学习到原汁原味的CUDA,同时能养成英文阅读的习惯。

今天的内容比较特殊,因为这个部分并没有出现在NVIDIA 在线版的《CUDA C Programming Guide》,但是如果你下载了CUDA,里面会带一份PDF电子档的版本,你会发现这个版本确实有这个章节。这个章节蛮重要的,虽然我们不明白为啥没有出现在在线版里,我们还是决定讲一讲。

本文备注/经验分享:

本章节主要涉及两点, 一个是新引入的nanosleep, 另外一个则是一个锁的优化版本的实现.从以前的章节中, 大家都知道, 只要NV开始引入的东西, 那么一般都会在后期发挥重要用途. 例如前几天我们刚刚说过的warp vote, warp shuffle, FP16半精度,以及还有更早的INT8之类的. 而今天的__nanosleep()扩展函数, 则是从CUDA 9.2和计算能力7.0+开始引入的新硬件特性.允许你的一个或者多个线程(请注意不是warp)进入暂停调度状态.也就是字面意思的休眠, 过了一定的纳秒后(如果是1Ghz的GPU频率, 那么正好1ns等于1个周期, 其他频率请读者自行换算) 读者可能会问, GPU不是应当充分忙碌才好吗?不是你之前发的章节, 还要要通过TLP(线程间并行), 和ILP(线程内部指令并行)来提高GPU的忙碌程度, 来掩盖延迟吗?为何NV却在新卡中反其道而行之, 弄了这么一个暂停调度(也就是说, 将暂停执行)一定时间的函数? 因为有的时候, 一些特殊的算法, 并非是越快执行越好的. 本章节后面实质性的给出了一个例子.是一个互斥锁(mutex)的实现. mutex是互相排斥的缩写, 指的是计算机程序里面,用来保护某些不能被同时访问的资源(例如, 你需要同时将2个不同位置的int都+1, 统一完成后才能继续, 中途不允许被打断,而GPU常规状态总是大量线程在并行的, 不使用一定的手段---例如本章节的互斥锁, 是无法做到的),有了这种互斥锁后, 才能在越来越复杂的GPU环境中,例如GTX1080才20个SM, 到了Titan-V就已经80个SM了,使用越来越复杂的算法.而本次NV引入的NANOSLEEP, 则可以有效的提升该mutex锁的性能.实际上, NV官方曾经在今年春天的时候,推介过一次这个.当时有个图, 我印象还很深刻(不是GTC), 使用了新的__nanosleep()后, 竞争mutex剧烈的应用场合,性能提升了大约10-100倍.因此可以看出此新扩展函数, 还是很有用的. 考虑到很多读者并非来自CS行业, 我们继续简单的说一下后面这个例子,实际上这个例子在之前的原子操作章节(前几天)遇到过.只是今天的这个章节上, NV提供了一个稍微优化点的明确的mutex版本. 如同任何锁一样, 都具有2个基本操作, 和如下流程: (1)基本操作1: 多个线程竞争一个锁的所有权, 互斥锁(mutex)最终只能有1个归属权. 也就是最终只有1个线程能竞争成功(如同很多同时追你的帅哥们, 最终只有1人会成功) (2)取得锁的所有权的线程(竞争成功的), 可以操作被该锁保护的资源(例如刚才说的2个int, 都把它们+1) (3)基本操作2: 完成操作过程后, 可以直接释放掉所有权. 此时可以循环到1, 其他线程可以继续竞争得到刚才释放掉的所有权(等于一个妹子分手了, 另外一个帅哥就可以上来竞争了) 这是一个典型的mutex锁的使用流程. 对于一些不能直接通过前几天的原子操作章节里面的基本原子操作函数完成的任务(例如刚才说的2个int的+1, 而不是1个---后者可以直接原子操作) 需要使用类似这种风格进行. 所以你看到了, 实质上的对mutex的利用其实可以明确的分成两点: (1)竞争锁和释放锁, 这往往是由成熟的库或者自行写的代码或者第三方提供的代码完成(例如本章节NV提供的); (2)你自己夹杂在中间的处理逻辑. 这个是用户自己提供或者说进行的; 所以一般的来说, 我建议大家尽量将前一部分采用公开的成熟的实现, 这样比较安全.而中间的部分则是自己可以发挥.这样对你好,也对我们好. 这是对锁的简单介绍.(互斥锁. 其它种类的锁请自行谷歌) 回到NV的具体代码, 我们可以简单的分析看到: mutex是一个锁(的位置), 里面是通过存放0或者1代表锁空闲, 可以被取得; 还是锁当前被占用的状态(本章节没说, 但是从代码中可以看出来). 而NV已经为你写好了加锁和解锁函数, 在加锁函数中: while(atomicCAS(mutex, 0, 1) == 1) 而解锁函数中: atomicExch(mutex, 0); 我们首先可以看到, 该锁的2个函数是通过基本的原子操作函数来构建的. 这也是我们前几天我们在原子操作的章节里说, 这些基本的原子操作函数是实现高层逻辑的基础.然后再请注意, 竞争取得锁的过程是一个循环, 反复尝试.而释放锁的过程却是一句简单的普通语句(无循环). 大家可以想想为什么. 其实很简单, 因为互斥锁每时每刻都只能有1个线程取得了所有权, 其他线程都在失败后(未能取得)继续循环, 尝试下一次机会(还记得刚才的帅哥追妹子的例子么). 因为mutex这里指向的位置, 当存放为0的时候才代表能取得, 只有当atomicCAS的结果返回是0, 不是1, 才代表成功了. 而一旦成功, 就立刻改变里面的值为1(被占用),其他人只能继续尝试.而解锁的时候, 因为理所当然的有所有权, 直接设定为0即可. 这是一个很简单的过程.如果刚才的循环while后面直接是分号. 该锁的2个函数(加解锁)已经可用了. 但是没完, NV在括号里面引入了__nanosleep(), 很好的提高了该锁的性能(还记得刚才说的NV春天发布的推介么),这里实际上存在一个忙等和避让的概念. 因为你知道, 刚才我们说过, 使用该锁是一个三步的过程: (1)加锁, 取得所有权. (2)慢慢处理被锁保护的资源 (3)处理完成后, 解锁. 实际上你可以看到, 一旦一个线程取得了锁, 就会进入过程2, 而此时过程2可能消耗不定的时间, 例如可能需要20ns才能完成. 如果此时, 其他未能取得所有权的线程, 在这个20ns时间内, 依然反复尝试取得锁, 实际上是一种浪费(因为完全不存在可能)——就如同一个妹子刚刚确定了恋爱关系, 你还在不停的献殷勤, 显然是无用的。 此时持续的竞争的这种浪费, 占用了宝贵的SP资源, 也占用量宝贵的功耗预算.而此时如果像NV这个代码这样, 加入了一定量的时间的等待, 则可以释放出来宝贵的SP周期, 例如可以贡献给竞争成功的那个线程更快的执行, 让他能尽快的释放掉锁.反而可能对性能和整体功耗有利.这就是为何之前开头的时候说, 有的时候并非让GPU不停的忙碌就好.(这就如同你估计妹子谈恋爱大约经过1个月后就会分手, 你可以先不理这个妹子1个月, 然后1个月后再考虑重新追她. 更有效率的),所以你看到, __nanosleep还是很有用的.不仅仅适合对于锁.对于很多其他lock-free的算法, 一些需要轮询(polling)的场合(轮询就是程序反复查询一个状态是否成立或者满足),也可以考虑使用它. 都是很好的应用场景. 本章节大致说了这些, 但实际上, 我们不仅仅只应当看到这么一点.

__nanosleep目前被编译为单独的一条指令(nanosleep指令, 同名) 从之前我们曾经说过的7.0+开始放宽SIMT的限制了(还记得我们之前说过SIMT带来的弊端和代价么? 还记得为何新版本的一些函数变成了__sync后缀了么?),到今天__nanosleep的提供.从本质上来说, 7.0+的GPU越来越靠拢CPU的核心的执行风格了.(例如某种程度上不再绑定32个线程在一起, 而像CPU那样能自由执行) 所以今天引入了__nanosleep()来支援一些原本CPU上常用的算法也很正常了. 所以不仅仅传统的GPU代码可以很好的运行, 一些更接近以前的一些CPU风格的代码或者算法, 也开始有用武之地了.这是一个很好的改变. 所有的CUDA用户都应该迎接这种改变. 以及, 本章节还需要注意的是,这里的休眠引入了指数避让.第一次避让8ns, 失败后避让16ns, 再次失败后避让32ns,直到最大限制256ns,还有其他的避让方式的. 感兴趣的用户可以自行搜索.

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

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

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

原文发表时间:2018-07-20

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

遨游浏览器把全球用户的这些数据偷偷传回了北京服务器

其实咱中国的Maxthon遨游浏览器在国际上也还是有一定知名度的,从StatsMonkey前年的数据来看,它在中国的市场份额排名第6,在波兰的份额也是第6。 最...

2159
来自专栏Java帮帮-微信公众号-技术文章全总结

Java面试复习大纲2.0(持续更新)

想要成为合格的Java程序员或工程师到底需要具备哪些专业技能,面试者在面试之前到底需要准备哪些东西呢?本文陈列的这些内容既可以作为个人简历中的内容,也可以作为面...

5247
来自专栏Linyb极客之路

你还在用if else吗?

面向过程设计和面向对象设计的主要区别是:是否在业务逻辑层使用冗长的if else判断。如果你还在大量使用if else,当然,界面表现层除外,即使你使用Java...

1964
来自专栏Spark学习技巧

案例简介flink CEP

随着无处不在的传感器网络和智能设备不断收集越来越多的数据,我们面临着以近实时的方式分析不断增长的数据流的挑战。 能够快速响应不断变化的趋势或提供最新的商业智能可...

6772
来自专栏Java技术栈

春节跳槽最新Java面试题及答案整理

大年初七好,今天大部分码农同学已经上班了吧,最近也是跳槽人才流动的高峰期,拿了年终奖,找找更好的机会。 小编也面了几家公司了,回来整理下面经分享给大家做个参考。...

3577
来自专栏Java帮帮-微信公众号-技术文章全总结

Java面试复习大纲更新1.0(持续更新)

1、背熟你的简历 原因:面试的第一个问题,一般都是让你简单介绍下你自己,或者介绍一下你最近的项目,而一个面试者,如果连自己的简历都无法熟知,对里面提到的项目、技...

4924
来自专栏LET

百词斩数据之小析

3775
来自专栏我是攻城师

关于Elasticsearch里面聚合group的坑

4856
来自专栏吉浦迅科技

DAY53:阅读Profiler Counter Function

Each multiprocessor has a set of sixteen hardware counters that an application c...

832
来自专栏互联网技术栈

Dubbo 3.0 即将到来

据了解,新的 Dubbo 内核与 Dubbo 2.0 完全不同,但它兼容 2.0。Dubbo 3.0 将以 Streaming 为内核,而不再是 2.0 时代的...

1012

扫码关注云+社区

领取腾讯云代金券