关于PCIE延迟和性能

我只是对“何必在乎延迟”这一点感觉上难以接受。
其实做程序就是做细节,习惯了。
每一个微小的细节都需要反反复复的长期思考。
您的何必在乎观点跟我个人有些出入,我就是关注细枝末节成习惯了。
不好意思,以后可能还需要向您请教一些细枝末节。
如果您觉得没用,就可以直接跟我说这个无关大局;如果您感兴趣,也请您不惜赐教。

"只能陪您到这里,远处就要开始下雨的风景…”

嗯嗯。只要我懂的。一定会告诉您。
(实际上我也喜欢细节。真的。不骗你。)

1: 您的每一楼我都仔细看过的。

2:十分欣慰您知道延迟可以被其他warp隐藏这一基本观点,这可以建立一个共同讨论的基础。

3:您写的“只是其他WARP加起来总和也根本无法隐藏的情况。”——是的,极端情况总是有的。

4:您写的“如果访存较多,这种情况很常见,延迟成为瓶颈是一种没优化程序的常态。”——“访存较多”和“其他WARP加起来总和也根本无法隐藏的情况”“很常见”并非是因果关系。访存很多的即使没有仔细优化过的程序也可以完全没有访存瓶颈,更不要说此瓶颈一定来源于延迟。也无法说这就是“常态”。至少在CUDA编程上如此。

5:关于您提到的“zero copy”,如果我没看错的话,是您在11#才引出的,本不在顶楼的讨论中。而且我们谁也没反对过“zero copy”的低效,反之曾多次强调“zero copy”延迟高,带宽小,推荐copy到global memory使用。但是,您不能拿11#才引出的,而且不是访存主流用法的“zero copy”这样一个极端的例子,来反复强调延迟如何如何不可掩盖。

6:关于您提到的“如果提前copy,延迟就只有一次,而不是内核计算时的很多次。”——此段无法理解。

大致如上,也希望LZ仔细考虑下我们的每楼回复的内容。
祝楼主编程愉快!

关于访存延迟是否成为瓶颈,这个跟访存与计算的指令密度关系很大。
一般程序,如果没有优化,我想这个比例能够达到4:32可能已经很不错了。
当然不排除有些程序本来计算密度很高的情况,如果能够达到4:300,当然延迟会自动隐藏,而无需预读。
如果程序这个密度是4:32,哪怕或者是4:100(假设自动隐藏门限为4:200),如果没有预读等优化,那延迟就一定成为瓶颈,无需测试。
我不否认有算法高计算密度的情况,可能您很幸运所以没有遇到延迟瓶颈问题。但不代表延迟是一个简单无需考虑的问题。

当然了,现在有了L1,常数,纹理等缓存,这个矛盾似乎有了很大缓解。还有编译器可能也在暗地里做了优化也未可知。

此外,作为提醒,我建议您阅读一下20楼我的回复。如果您已经阅读过,请无视此消息。

OK,多谢!
:handshake

我觉得您多少还是受到CPU执行的固有思想的影响。
也许在CPU的执行模式下,有您所说的问题,因为CPU一个核心上通常只能有1~2个线程。遇到长延迟的操作可能需要等待。(不过CPU的大容量的cache可以解决一些问题,超线程和乱序执行也能改善此问题)

但是在GPU上,有大量的线程。以fermi核心为例,一个SM上,只有32(SM2.0)或者48(SM2.1)个SP,以及一些其他单元。但是一个SM上最多可以驻留1536个线程,完全可以通过线程warp的自动切换来解决常见的访存延迟。而且NV保证,这种切换是无开销的。请LZ看一下20#中,横扫千军版主提到的运行状态,如果这些SM都能跑满,那必然访存是OK的;反之,如果卡在的访存延迟上,SM必然是空闲的。这里并没有找计算十分密集特殊情况。
以及,如果在kernel里面手工适当提前访存时机,在真正使用前的某个位置提前读取,那么可以减少掩盖此延迟所需要的线程数(但有可能付出多用寄存器的代价)。这种做法称之为ILP(instruction level parallelism),此做法在前面也多有提及。使用此方法不是必须的,一般建议是多上线程,自动完成掩盖工作。

我的解释大致如上,祝您在论坛过的愉快~

既然您提到了,我再重复一下我的理解:
说WARP切换能够很轻松的隐藏访存延迟问题其实是一个天大的笑话!
我认为WARP切换充其量能够隐藏指令执行延迟,也就是那11个周期,差不多了。
周期再多,达到数百的时候,WARP切换根本无法隐藏这个数量级的延迟。
除非程序本身计算密度较高。
版主不妨仔细计算便知分晓。

既然您认为是“天大的笑话”,请放声大笑吧。

祝您在论坛过的愉快,您就当这里是娱乐论坛好了。

线程配置:63寄存器;SM上WARP数:32。
延迟:800。
每周期指令数:8
800周期指令数:8008。
隐藏800周期需要指令数:800
8/32=200。
也就是说这32个WARP每个都要执行200条指令,延迟才能够被隐藏。
提前200条指令预读是必须的。要么就是计算密度很高,每访存一次200条计算的比例是必须的。
不知道我的如上计算哪里有错,请指出!

当然,WARP切换在一定程度上平摊减缓了延迟的影响,这一点倒是真的。

话说楼主一直在试图暗示论坛,降低延迟,好处是大大的有。

但是我不这么认为,如同我指出的,您只需关心吞吐率即可。

我们具体说,有如下代码:
LD R4, [R2];
IADD R4, R4, R4;
LD R4, [R2 + 4];
IADD R4, R4, R4;

LD R4, [R2 + 4n];
IADD R4, R4, R4;
此代码是1:1配比的。

你可以看到,每个IADD加法指令都在等待上一个访存读取完成。的确卡住。也的确是在等延迟。可是我不认为这个对性能有何影响。why? 因为此时L2到SM的port已经满载了。

此时已经卡在了L2的吞吐率上了。

您此时突然用改造SM, 使得内部的延迟成为0,也依然没有意义。以为您的卡的L2已经满载了。

换句话说,IADD在等R4, 等1年,和等10年没区别。因为最终是按瓶颈的吞吐率算的(瓶颈是您的L2)。

我举个例子,您的城市分为东西2个城,中间只有一个只能1秒通过1人的小桥链接,而桥很长,需要1年才能走完。
人类住在西城,女神住在东城。

女神的工作是召见西城所有的人,必然1天能召见的人是有固定的最大限度。您可以说,我经历了巨大的延迟,都1年了。可是您依然不影响女神一天能工作量。您说呢。

以上访存占据了一半的代码和这个类似道理,延迟固然在,的确无法掩盖,但您的卡的L2, 已经100%忙碌了。您再优化什么延迟,什么提前量,依然不会让这个kernel更快的完成。您说呢?

所以说,您的“必须降低延迟论”是不对的。如同我20#所说,只要卡能全力以赴,人们就高兴了。

您的计算有错误。你一共需要6400条。这6400条来自1024个线程,自然每个线程需要6个周期。

很好,您的解释很精彩!
其实就是带宽和延迟到底哪个是真正的瓶颈的问题。
按照带宽来算的话,是每字节8条指令的比例。
如果程序设计好,可以按照字来访问,也就是每字32条指令的比例。
也就是说最好情况下,带宽可以按照每字32条指令的比例能够比较顺畅了。
您觉得,32与200比较,是不是还是有很大的差距呢?

(1)想让您的SM的SP和LSU都满载。您需要16B数据:32条指令(sm_20)的配比,也就是在2.0上您需要1字节:2指令的配比。

但很遗憾,profiler给出的数据远远小于这个配比,why? 因为L2的port数目有限,不是所有的SM都可以在同一个时刻使用1条到L2的port的。例如192bit位宽的某款GTX460, 通过电镜发现了4条可用的port, 但却有7个SM. 这样导致您的配比的分母要扩大。

那么怎么办呢?如果对一卡计算合适的配比呢?
考虑如下事实:L2的总吞吐率(对大部分的卡,小部分卡不适用)和global memory的总吞吐率(读+写)就高了一点点。而后者是容易得知的。所以:

(2)想让您的GPU的SP和L2都满载,您大致需要您的显卡的global memory总吞吐率 : 您的SP数目*频率。例如192-bit, 1.45Ghz的SP频率,有336个SP的GTX460, 需要86.4GB/S : 336 * 1.45Ghz = 1字节:5.6条。

这个公式也是profiler用来建议您的合适的指令配比时候所用的。

神马?我不愿意为了配比而配比!我的算法本来就是访存多(或者计算多), 那怎么办?

那么您可以不配比,例如访存多,就让SP空闲就好了,还能省点电。

:slight_smile:

补充(您的固定数据1:8之类的不准确。实际上这个数据和具体的卡有关。)上文忘记说了。:slight_smile:

我是按照带宽200GB/S,1.6T计算能力大约估算的。我的一切计算以kepler为准,Fermi的早已经忘光了,也不作考虑。
1字节8指令,1字需要32指令隐藏。
当然,如果程序访问的是字节,那就是1字节需要8指令隐藏。
这样,代码每访存一次8-32条指令隐藏这个带宽,但是却需要200条指令来隐藏这个延迟。

先说一下您的第二部分:“代码每访存一次8-32条指令隐藏这个带宽,但是却需要200条指令来隐藏这个延迟。”
我表示无法理解您的隐藏,和带宽这些词这里是神马意思。我建议您继续看看女神楼层或者20号楼层。我们现在在37号楼层。

对于其他部分,您又在偷换概念。

从您的“这样,代码每访存一次8-32条指令隐藏这个带宽,但是却需要200条指令来隐藏这个延迟。”这句发言中,隐含了:
“如果能隐藏,能效会更高”的意思。

但是如同我提前指出的,您的卡都按200GB/S跑满了算了,不能更高效了。已经卡在这个瓶颈上了。

如同一个有4个木片拼起来的木桶,1个木片高30厘米,剩下3个高50厘米,然后您说,还可以继续加高50厘米的木板(如同您可以继续在本来需要等待延迟的计算指令里继续添加一堆指令免费算)。但是意义何在。您的代码不能跑的更快了,反而只能浪费电。

我是说,按照带宽来算,保持每访存一次8-32条指令这个比例,基本顺畅。
但是按照延迟来算,需要保持每访存一次200条指令的比例,才能顺畅。

您依然强调您的“搞好延迟就搞好了社_会_主_义建设”的论点。
但是我已经反对过多次了,请看上面众楼,例如女神楼和20楼的说法,只要能运行到某组件的极限,何必又搞些其他的不能解决瓶颈的东东呢!

为了表示对您的尊重,这里直接用您的数据说开吧。

既然您说的1:8-32已经可以让访存满载了,您又神马加到到200条指令干嘛呢?

您的算法总是存在的。必然有一定的访存和计算比例。在访存满载的情况下,无辜的加多计算指令,何必呢?

当然,时至冬日,多耗电发点热还是好的。

请君三思。