、4个队列的、4个大厨的食堂,每个队列有几个客人? - 16个。
哪个快?应该是第一个,因为同时8个服务生,交错开接单,当然能减少某个客人犹豫、磨磨叽叽带来的延迟。让4个大厨忙个不停。
别忘了,我们之前已经探讨了,开启超线程以后,因为增加了4个服务员,会带来额外的开销 - 每个客人入队前都会犹豫,都要花时间思考 - “两个队我究竟应该怎么排?哪个队人少?哪个服务生看的养眼?。。。。”。这种额外的开销(处理延迟,性能损耗)是硬件级别的,是英特尔设计CPU的时候就规定死了的。我们任何事都无法解决硬件方面的问题。而唯一的办法就只能是 ----> 关掉HT。但关掉HT,每个队列变成16个客人,而每个服务生,从接待8个客人,增加到16个客人(AS延迟从8份,增加到了16份),怎么破????
重头戏来了,硬件我们当然无法改动,但是软件程序上我们可以进行优化,我们可以重写程序的并行调度算法,使得程序最大程度上针对CPU天生的硬件架构进行优化。具体的算法上细节太专业不容易懂,我举下面这个例子一说,你可能明白了:
例子:
比如来了64个客人,每个人都想吃一个盖浇饭。来到一个4个服务生、4个队列的、4个大厨的食堂。每个队列会有几个客人? - 16个。
好,对于每一个队,现在我不让这16个人都去排队,而是从队里面推选出1名代表,让这个代表代替16个人去向服务生点单。一个单子上16份盖浇饭,其余15人退后。这样一来,总共只有4个客人(代表)点单,其余的60个人在下面歇息。而点单速度方面,每个队最多也就(一个代表)磨叽一次。后堂大厨接到16份盖浇饭的订单,也只有拼命做的份。你总不能炒一个盖浇饭歇5分钟吧。。。
瞧, 是不是问题解决了?
1)既避免了8个服务员、开8个队列所带来的AS额外开销
2)也最大程度的利用了大厨(减少了PU的闲置时间)
作为一个超算系统,大家都在追求极限性能。世界上每年都会进行500强超级计算机性能排名,一点点的性能差异都有可能会让你的排名退后不少,所以大家都需要尽可能地压榨系统的最后一点性能。
同时,这个实例也告诉DIY玩家们,硬件重要,软件也重要。硬件强悍的同时,软件(驱动)也要进行相关的优化。如果软件没有针对性的优化,再强的硬件也发挥不出100%的威力。这个也从侧面解释了为什么有些硬件,属于跑分王类型。比如测试3Dmark这种,得分暴高,而一到实际游戏中,表现一塌糊涂。
买硬件,要买用的人多的,不要搞太小众的东西。
软硬兼施,不仅硬件性能要强,软件优化也要做到位
1)相对于4核8线程(开超线程),4核4线程(关超线程)后在处理(调度)多线程方面的劣势,我们完全可以通过修改源代码,把这个劣势给抵消掉。而8线程(多了4个硬件AS)所带来的硬件架构方面的额外开销,这个可以理解成集成电路级别的,我们无能为力。
所以就像华山的剑宗和气宗。
剑宗就是:简单的增加程序线程的数量,同时打开CPU超线程功能。
气宗就是:修改程序,做算法上面的改变,手动的计算运算周期,调整并行策略,将延迟隐藏掉。
剑宗速成,气宗慢成。同样练1年,剑宗练到6级威力,而气宗只能是3级。但是如果给足够时间,剑宗的极限只能练成9级,就无法突破了。而气宗最终可以练到10级。
2)还有就是优化。这里面牵涉到一个平衡的问题。性能 vs 通用性。
举个简单例子,如果给你一个加法运算:
1+1+1+1+1+1+1+1 (8个1相加,当然现实中,这么小颗粒度的运算根本没有必要进行并行,不值得。这里是为了举例需要。)
第一种方案 (性能低下+通用性最高):
什么优化都不做,程序员只要小学一年级毕业就行了。程序太简单明了了,一行搞定,扔给CPU,做了7次运算,算一次1秒钟,这样就是7秒钟。多核一点用都没有,完全是拼单核性能。强调一句:一个单序程序(serial program)比如8个1相加,你不在代码级别做并行化,它不会自己变成一个多核程序。也就是说:它只会用一个核!!!这里,没有奇迹,没有魔术!怎么做并行化?改你的程序,用上pthread, fork, MPI, openMP。。。等很多种方法,具体细节不多说了。感兴趣的话求助一下度娘。
总时间:7秒钟
第二种方案 (良好性能+高通用性)
做并行优化:
1)首先,数一下你的机器有几个核,这里假设只有物理核。好,数好了,有4个核,花费0.5秒钟 (时间值只是举例)。
2)这时,我可以根据核的数量(=4),把运算劈成4份,产生4个(程序上的)线程,变成下面形式,从而和硬件核心个数(=4)进行1:1匹配。这样的调度开销0.5秒钟,
3)然后,开始下面的计算
(1+1) (1+1) (1+1) (1+1)第一轮每个核都是一次运算,共1秒钟
(2+2) (2+2) 第二轮1号2号核都是一次运算,共1秒钟
(4+4) 第三轮只有1号核工作 共1秒钟
花了1+1+1=3秒。而且这个也是有数学证明的(Divide and Conquer问题,具体细节不多说了。感兴趣的话求助一下度娘), 相信大家都学过对数,Log28 = 3 。
总时间:0.5+0.5+3=4秒钟
第三种方案,比较极端 (极限性能+低通用性)
如果,我知道我的系统里面有4个核,是不是:1)数多少个核 2)调度开销 全都可以省了?好,这些费时间的步骤全部去掉。直接奔步骤3)。
----> 这样,最终只要3秒钟。
但是,这种方法只适合4核的机器,如果你给他双核或者8核的机器,整体速度会大打折扣,还不如第二种方案,因为第二种方案带有一定的通用性和自适应性。而第三种方案是“死”的,无脑的。也就是编程时候的hard coding (翻译过来叫死码或硬码),这种编程习惯不推荐,因为写出来的程序实用性会很差。
现在不知道你看出点端倪来了没有?
其实,我们系统测试追求的是第三种方案,因为我们非常清楚自己系统的架构。完全不用考虑什么比如双核和4核CPU的情况(我们一般多用8核的U)。CPU中二级三级缓存的大小都是固定的。也就是说我们的代码优化可以是非常极端的,完全面向特定型号硬件的优化。这样的优化换来的就是低通用性,也就