- 论坛徽章:
- 1
|
本帖最后由 bensenq 于 2012-12-12 00:01 编辑
回复 12# leslielg
你认为那样写的原因是处于逻辑正确性考虑么?我完全不赞同,我认为仅仅是性能优化考虑,就像上面几个帖子描述的那样。
你说“否则如果一直从寄存器读取lock的值,那这就是个死循环了。”我认为这个理解是完全错误的.
while (lock != 1); 这句代码的语义是:只要lock的值不等于1就继续检查lock的值,直到判断到lock为1再执行后面的指令。lock变量当然是在内存里,任何一个可用编译器都会按照该语义要求CPU循环从内存里读取lock值,而不是所谓的"一直从寄存器读取lock的值"。
直接上实验吧:
test.c- int lock;
- int main()
- {
- while(lock != 1);
- return 0;
- }
复制代码 编译,反汇编为test.s
gcc test.c
objdump a.out -d > test.s
直接跳到main函数:- 080483b4 <main>:
- 80483b4: 55 push %ebp
- 80483b5: 89 e5 mov %esp,%ebp
- 80483b7: 90 nop
- 80483b8: a1 18 a0 04 08 mov 0x804a018,%eax
- 80483bd: 83 f8 01 cmp $0x1,%eax
- 80483c0: 75 f6 jne 80483b8 <main+0x4>
- 80483c2: b8 00 00 00 00 mov $0x0,%eax
- 80483c7: 5d pop %ebp
- 80483c8: c3 ret
复制代码 可以发现while循环对应80483b8~80483c0处的3条指令,分别读取lock值(访存)、比值较和跳转功能,是不是没一次循环都重新读取lock的值呢?到这里我想结论已经非常明显了:程序不会一直从寄存器读取值,因为这违背了这条C代码的语义。当然CPU每次读取lock的值也不一定都会访Memory,因为CPU和Memory之间还有Cache,只要Cache命中就会直接从Cache中取以节省时间,这也是我为什么提Cache一致性的原因。
回过头来我们在来验证一下楼主的疑问,看看内嵌汇编的写法到底带来了那些优化。
test1.c- int lock;
- int main()
- {
- while (lock != 1) {
- __asm__ __volatile__("rep;nop": : :"memory");
- }
- return 0;
- }
复制代码 编译,反汇编为test1.s
gcc test1.c
objdump a.out -d > test1.s
main函数:- 080483b4 <main>:
- 80483b4: 55 push %ebp
- 80483b5: 89 e5 mov %esp,%ebp
- 80483b7: eb 02 jmp 80483bb <main+0x7>
- 80483b9: f3 90 pause
- 80483bb: a1 18 a0 04 08 mov 0x804a018,%eax
- 80483c0: 83 f8 01 cmp $0x1,%eax
- 80483c3: 75 f4 jne 80483b9 <main+0x5>
- 80483c5: b8 00 00 00 00 mov $0x0,%eax
- 80483ca: 5d pop %ebp
- 80483cb: c3 ret
复制代码 while循环对应指令为80483b9~80483c3 4条指令,发现区别了吧?其实就是在每次读lock值前多了个pause指令(第一次除外,见80483b7处jmp)。关于pause的用途,楼上好几位都说过了,主要影响多核、多线程下的CPU性能及功耗,和CPU体系结构很密切,我也小google了一下,stackoverflow上有个回答供大家参看:How does x86 pause instruction work in spinlock *and* can it be used in other scenarios? |
|