之前对这两个名词有误解,没有多么深刻的认知
Compute bound: computationally intensive, the time involved in the computation is much longer than I/O operation, e.g. linear and conv. Memory bound: the time involved in I/O operation is bigger.
直到今天问了mingfei大神~~
首先关于这个IO的概念是不对的,
IO指的是从硬盘drive
到内存memory
,IO
的带宽是有PCI-e
决定的,PCIe 4.0的带宽是上行下行各8GB/sMemory BW
是从内存到cache
,这个就是你内存条带宽 * port
数量,一般CPU是100+GB/s; GPU有HBM(high bandwidth memory)
v100好像是一千多
一般L1的带宽应该是两千多,多以利用好你的L1而不要让code等在Memory的BW上。。。
比如你的CPU 每个cycle可以进行1次FMA
的运算,但是每个cycle只能load 1个float (4个Byte)
那么。。。
1 | a = b + c |
这个算式是被什么bound呢,这里只有一个add能不能在1个cycle内完成呢?
答案是否定的,因为要读b和c(8 byte), 写一次a(写实际是一次读一次写,就是8byte)。。。
这样你就应该很容易理解
我们的CPU peak ALU吞吐量在9Tflops/sec左右,mem带算宽180GB/s,这个ratio就是:
1 | Ratio = 9 * 1024 Gflops / 180 Gbytes = 50 |
那么一个byte对应50次fma
,高于这个ratio的是compute bound
,低于这个ratio的是mem BW bound
。这个叫static analysis
实际情况要复杂很多。
做性能预测的时候大概就是这个算法,比如一个gemm,不一定是compute bound,MNK如果又一边很小,就是mem BW bound…
一般硬件在设计的时候都会让o(n3)的运算compute bound。我们可以对一个kernel进行详细的分析:
- 多少次计算
- 多少load
- 有没有重用(cache影响,有的话是L1/L2/LLC那一个位置)
有了上述条件你就可以算出理论性能上限。。。
当然大多数情况下我们根本达不到理论上限, 为什么呢??
一般写的比较好的kernel在vtune里面查CPI(cycles per instruction)应该在0.5以下,如果出现十几这种情况一般是: 有频繁函数调用,访存不连续,还有就是慢速指令(比如整数的除法或者取模,非常慢)
再举个很简单的例子比如fuse 一个kernel:
1 | a = a * alpha |
假设a和b都是n长度的tensor.
那么地球人知道要fuse起来比较快,为什么呢?这里我们不考虑fma的情况,就假定这是两次运算
- 单独计算的时候要读a两次,读b一次,写a两次
- Fuse起来a读写各一次,b读一次
如果有复用的情况还要考虑cache(这就是大师说的对于L1的优化,L1速度比reg慢一下但差不多)
这时候就要做Blocking,如果做得好还可以规避掉leading dimension的问题。。。这个问题很常见,因为AI里面很多都是256, 512, 1024这种数,很可能下次循环就把上次循环cache的一条数据给flush了。。。
详情请关注大佬即将推出的LayerNorm和FusedLSTMKernel…