作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
很早就建立多核、多线程的博文目录,但一直没有写相关文章,后来干脆把这两个目录合为一个,叫做并行编程。
今天就开始第一篇吧,写一点自己关于提高程序并行效率的一点经验。并行编程从多线程编程,多进程编程,直到现在的多核编程。目前多核CPU应用已经越来越广泛了,大部分的电脑都是多核了。所以提高多核下的程序并发性,可以极大的提高程序的效率。
先从最基本的锁说起吧。在多核下,锁的代价比以前更高了,这个稍后会提到。为了提高并发性,最好的追求是无锁。如果锁是无法避免的,那么要让锁的粒度尽量小。
1. 如何做到无锁呢?
1)一般我们可以牺牲空间来换取性能。比如一些统计信息,可以建立per CPU的变量,即每个CPU都有一份统计信息,当计算总的统计信息时,需要加上所有的per CPU的统计信息。
2)可以使用RCU机制——RCU的具体用法,问google去:D。使用RCU机制,可以避免锁的使用;
3)良好的设计。举个简单的例子,读写队列。读线程和写线程分别维护两个指针,来实现队列的入队和出队。
2. 缩小锁的粒度
当锁的粒度越小,竞争发生的概率也就越小,程序的并发性也就越好。举个简单的例子,一个hash表。如果只有一把锁用于保护hash表,那么为了提高并发性,可以为每一个桶slot建立一把锁来取代原来的hash表的全局锁。显然,后者的并发性一定强于前者多倍。
3. 选择恰当的锁
1)如关键代码运行时间很短,可以使用spin lock,来取代mutex或者semphore。原因有二,一个是mutex和semphore为系统调用,这个cost较高,需要用户态和内核态的转换。另外一个原因是当等待锁的时候,该线程会被加入到sleep对列。这两个原因都影响了并发性。当然,如果关键代码的运行时间比较长,就不要使用spin lock,而使用mutex或者semphore。因为spin lock时,等待的线程是一直自旋spin,这白白浪费了CPU的资源。
2)如果锁包含的对象,在被修改的过程中,可以合法访问——即不造成程序crash,那么可以使用seqlock。seqlock对于写者来说,是一个真的锁,即不允许多个写者同时修改对象。但是对于读者来说,根本没有加锁和解锁的过程,读者通过检查锁的seq number,来检查对象的数据是否一致。seqlock对于读取多修改少的情况,非常适用。
关于CPU的cache。对于多核CPU来说,每个CPU都有自己的data cache和code cache。这里指的是L1 cache,我记得L2 cache就是多CPU共享的了。那么当一个变量同时存在于多个CPU cache时,当一个CPU更新这个变量时,它的cache自然会被更新。但是因为多个CPU的存在,那么其它CPU的cache同样也需要更新。一般来说cache更新有两种机制,write-back和write-through。不同的厂商的CPU采取的机制也不同,但是更新cache的代价却是不可避免的。那么我们为了提高效率,就要尽量避免这种cache的更新。如何做呢?就是让变量不要存在于多个CPU的cache中。这个就有各种方法了。像上面提到的per cpu的变量,以及不同CPU使用不同的变量。还有上面提到的锁的问题。当加锁时,会强制CPU更新cache,来保证数据的一致性。
另外,cache除了有cache间的同步问题。还有一些情况,会导致CPU的cache被清除。
1)进程切换,即CPU要运行一个新的task,这时CPU会清空以前的cache;
2)发生中断,处理中断的CPU会清空cache;
应该还有其他的情况,会导致CPU清空自己的cache。但是我一时想不起那么多了,欢迎大家补充。
最后一个关于提高并行效率的方法就是优秀的架构和软件设计。关于这点也是听上去最虚的,没法具体说明,但是它确实最有效的方法。不过这个需要具体问题具体分析。一个简单的原则就是在硬件资源上进行划分,如内存,不同CPU使用不同的内存。对于软件来说,将整个工作,划分为耦合性很低的不同任务,使不同任务可以同时运行。并且可以利用CPU的亲和性,使某个task只在某CPU上运行,减少或禁止进程切换到另外一个CPU上。这样既保证了CPU的cache不会被清空,也使cache的命中率大大提高。
啰啰嗦嗦的说了一大堆,基本上自己刚刚对于并行编程的一点经验总结吧。做了比较长时间的多核编程了,一直没有认真的总结一下经验。现在这么一写,发现还是有不少东西值得再研究一下细节,再拓展一下的。等我以后的总结吧:D
阅读(11569) | 评论(13) | 转发(8) |