图形处理器(GPU)什么都能做,做得比中央处理器(CPU)好,为何不加上中央处理器指令替代中央处理器?
人类的程序中有20%的代码含有分支,处理分支代码的时候,alu再多也没有任何意义,因为你算的东西很有可能是不对的,你需要的是庞大的前端机构和大量的缓存来进行有效的分支预测,现代CPU可以对绝大多数常见分支实现几乎百分之百的预测(当然有一些很难以预测的逻辑比如说计算出一个下标来访问巨型数组这种东西还是难以解决)。
GPU这种东西是非常不适合于处理跳转体系的,你可以通过代码优化。用掩码取代分支等方法来解决一部分分支但你不可能解决所有的分支,这些分支的存在会使得GPU的性能变得非常低下。
拿cuda举例子,这玩意工作的时候就是SIMD的,所有的线程都是同一个指令发射出去操作不同数据,那想象一下假如说线程逻辑里面存在分支,但是这个线程块同一时间只能发射一个指令,要么所有线程都走case1要么都走case2(注意不存在有一些线程走1有一些走2的可能性,硬件上就不允许这种走法),那么假如发射的是case1的控制流,如果数据实际上需要走case2,那么这部分结果就全错了,那你还需要去辨认谁是错的,并且把错的线程上的寄存状态回滚,这些动作代价都是巨大的。
一个分支就这样了,那假如说这里的代码是好几层的if-else呢?如果输进来的数据足够随机,你最终在一个线程块上只会得到一个线程的正确结果,换言之现在线程块彻底退化到单线程。。。。而我们知道CPU的频率把GPU按着打,所以最后结局就是速度远不如CPU。你可以跟我说这种代码不常见,但是请记住,CPU是“通用处理器”,什么叫通用?就是可以尽量好的应对所有的情况,如果你告诉我有一些情况你应对不了,那你就不是通用处理器,只能叫专用处理器。
然后再进一步说,假如这里面的分支最后要执行的指令是访存,比如说case1访问0x0001地址,case 2访问0x0120地址,等等。甚至于访存的地址是根据输入现场算出来的,典型的比如说哈希查找表。
哦豁,完蛋,脸接访存延迟,甚至还有错误的结果和正确结果竞争访存带宽。而在CPU上,优秀的分支机构可以提前确定分支方向,提前把内存访好,数据放进缓存,等算到位置直接就数据供上去了。访存地址现场计算的,CPU会识别到这种特征,重排指令顺序,争取把访存指令跟接下来用访存结果的指令之间的距离拉开,在这里面填上代码的其他部分来掩盖访存的代价。
ps:超线程就是为了减轻访存代价而出现的。想象一个单核带超线程的CPU处理一个2线程的哈希查找表,假设算哈希的时间以及取到数据以后进行操作的时间等于访存时间,那么可以在线程1算出hash开始访存的时候切到线程2,线程2访存的结果已经准备在寄存器上了,现在开始处理线程2,完成操作以后计算下一个hash,算完的时候线程1那边需要的数据内存正好送到,那么现在线程2访存,切去线程1干活,整个过程算术机构完全没有停滞,需要的代价只是多一套寄存器组。而如果没有超线程的话就要脸接所有的延迟,算术机构利用率只有50%。
那如果你打算给每一个ALU配备一个单独的指令发射机构甚至是分支预测机构呢?
恭喜你,你得到了一块巨型CPU。
但是它的成本多半不会被任何芯片厂家接受。而且考虑到光速壁垒,这个CPU的上下部分是很难实现快速的同步的,在多线程运行的时候core1改了数据,core9999现在立刻要访问这个数据,不行,得等core1的改动广播到core9999这边,因为光速有限(1GHZ就是1ns一个周期,在这段时间内光只能走30厘米),这个信息传播的延迟会很严重,因此一个超大的芯片是没有任何意义的,你的频率做不高
蓝海大脑 京ICP备18017748号-1