linux编程的108种奇淫巧计-1(FALSE SHARING)【续】
2010-10-26 12:40
309 查看
上篇博客:http://blog.csdn.net/pennyliang/archive/2010/10/20/5953939.aspx提出了一段代码,并没有给出解释,本文接上文继续展开讨论。
该文有很多网友回复,比较集中的看法是CPU字节对齐,巧合的是有一个朋友用这个代码做了测试,发现对齐和不对齐的代码执行的速度是一样的,原因是他的笔记本安装的linux操作系统,而笔记本是单核的,所以就出现了这个状况,如果和CPU字节对齐,在单核的情况下怎么会速度一样呢?另外如果是CPU字节对齐,把线程去掉,替换成两个函数依次执行,也应该有效率的差异。
这是一种典型的FALSE SHARING问题,在SMP(对称多处理)的架构下常见的问题。SMP简单的说就是多个CPU核,共享一个内存和总线,L1 cache也叫芯片缓存,一般是私有的,即每个CPU核带一个,L2 cache可能是私有的也可能是部分共享的。
为了表明FALSE SHARE带来的影响,设计了这个简单的多线程程序,包含两个线程,他们分别做求和使用不同的变量,但由于cnt_1的地址和cnt_2的地址在同一条cache line中,实测环境中cnt_1的地址为0x600c00,cnt_2的地址为0x600c08,而cache line的大小为64个字节(cache line大小可以通过getconf LEVEL1_DCACHE_LINESIZE得到,或者命令cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size得到),这样就会发生FALSE SHARING问题。将两个变量在64字节对齐后,cnt_1的地址为0x600c40,cnt_2的地址为0x600c80,恰好错开在两条cache line上,源代码参加上篇博客。
FALSE SHARING问题在此前的博客也有详细讨论,可以参见:http://blog.csdn.net/pennyliang/archive/2010/07/27/5766541.aspx
最后,可能有读者会问,有谁会这么脑残写这样的代码,日常编码怎么会碰到这样的问题呢?我给大家说一个具体的场景,假如有一个lock-free的queues,里面包含了很多类型的queue,每个queue包含一个head和一个tail,这两个值分别被消费者和生产者之间竞争,因此如果不考虑false sharing问题,可能会造成低效代码。这样一个多线程共享的队列结构(多生产者,多消费者共享)用以下哪种结构更好呢?我不再公布答案,有兴趣的朋友可以去找找这方面的代码,看看他们是怎么写的。
【1】
struct queues{
type head1
type head2
...
type headn
type tail1
type tail2
...
type tailn
}
【2】
struct queue{
type head1
type tail1
type head2
type tail2
...
type headn
type tailn
}
【3】
struct queue{
type head1
type head2
...
type headn
type add_enough_padding;
type tail1
type tail2
...
type tailn
type add_enough_padding;
}
【4】
struct queue{
type head1
type add_enough_padding;
type tail1
type add_enough_padding;
type head2
type add_enough_padding;
type tail2
type add_enough_padding;
...
type headn
type add_enough_padding;
type tailn
type add_enough_padding;
}
该文有很多网友回复,比较集中的看法是CPU字节对齐,巧合的是有一个朋友用这个代码做了测试,发现对齐和不对齐的代码执行的速度是一样的,原因是他的笔记本安装的linux操作系统,而笔记本是单核的,所以就出现了这个状况,如果和CPU字节对齐,在单核的情况下怎么会速度一样呢?另外如果是CPU字节对齐,把线程去掉,替换成两个函数依次执行,也应该有效率的差异。
这是一种典型的FALSE SHARING问题,在SMP(对称多处理)的架构下常见的问题。SMP简单的说就是多个CPU核,共享一个内存和总线,L1 cache也叫芯片缓存,一般是私有的,即每个CPU核带一个,L2 cache可能是私有的也可能是部分共享的。
为了表明FALSE SHARE带来的影响,设计了这个简单的多线程程序,包含两个线程,他们分别做求和使用不同的变量,但由于cnt_1的地址和cnt_2的地址在同一条cache line中,实测环境中cnt_1的地址为0x600c00,cnt_2的地址为0x600c08,而cache line的大小为64个字节(cache line大小可以通过getconf LEVEL1_DCACHE_LINESIZE得到,或者命令cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size得到),这样就会发生FALSE SHARING问题。将两个变量在64字节对齐后,cnt_1的地址为0x600c40,cnt_2的地址为0x600c80,恰好错开在两条cache line上,源代码参加上篇博客。
FALSE SHARING问题在此前的博客也有详细讨论,可以参见:http://blog.csdn.net/pennyliang/archive/2010/07/27/5766541.aspx
最后,可能有读者会问,有谁会这么脑残写这样的代码,日常编码怎么会碰到这样的问题呢?我给大家说一个具体的场景,假如有一个lock-free的queues,里面包含了很多类型的queue,每个queue包含一个head和一个tail,这两个值分别被消费者和生产者之间竞争,因此如果不考虑false sharing问题,可能会造成低效代码。这样一个多线程共享的队列结构(多生产者,多消费者共享)用以下哪种结构更好呢?我不再公布答案,有兴趣的朋友可以去找找这方面的代码,看看他们是怎么写的。
【1】
struct queues{
type head1
type head2
...
type headn
type tail1
type tail2
...
type tailn
}
【2】
struct queue{
type head1
type tail1
type head2
type tail2
...
type headn
type tailn
}
【3】
struct queue{
type head1
type head2
...
type headn
type add_enough_padding;
type tail1
type tail2
...
type tailn
type add_enough_padding;
}
【4】
struct queue{
type head1
type add_enough_padding;
type tail1
type add_enough_padding;
type head2
type add_enough_padding;
type tail2
type add_enough_padding;
...
type headn
type add_enough_padding;
type tailn
type add_enough_padding;
}
在上次的博客linux编程的108种奇淫巧计-1(FALSE SHARING)我们给出了代码,但并没有解释,本文给出一些详细解释。
相关文章推荐
- linux编程的108种奇淫巧计-1(FALSE SHARING)
- linux编程的108种奇淫巧计-1(FALSE SHARING)【续】
- linux编程的108种奇淫巧计-1(FALSE SHARING)
- linux编程的108种奇淫巧计-15(减少复制)
- linux编程的108种奇淫巧计之18(SIMD)
- linux编程的108种奇淫巧计-4(编译展开)
- linux编程的108种奇淫巧计-7(再答gangban_lau)
- linux编程的108种奇淫巧计-4(编译展开)
- linux编程的108种奇淫巧计-4(编译展开)
- linux编程的108种奇淫巧计-7(Lock-free实验)
- linux编程的108种奇淫巧计-4(编译展开)(续)
- linux编程的108种奇淫巧计-8(嵌入汇编的另一种写法)
- linux编程的108种奇淫巧计-16(如何达到内存最大带宽,复杂指令)【续】
- linux编程的108种奇淫巧计-7(Lock-free实验)【答网友问】
- linux编程的108种奇淫巧计-4(编译展开)
- linux编程的108种奇淫巧计-2(RDTSC)
- linux编程的108种奇淫巧计-3(magic 2)
- linux编程的108种奇淫巧计-11(乱序)【续】
- linux编程的108种奇淫巧计-4(编译展开)
- linux编程的108种奇淫巧计系列被翻译成日文版了