亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

Chinaunix

標(biāo)題: 關(guān)于自旋鎖中的忙等的疑問 [打印本頁]

作者: dingyujie    時間: 2012-12-09 12:58
標(biāo)題: 關(guān)于自旋鎖中的忙等的疑問
我們知道內(nèi)核中,如果某個自旋鎖暫時獲取不到,就進(jìn)入一個忙等階段,大概的代碼歸納為:

while (lock != 1) {
    __asm__ __volatile__("rep;nop": : :"memory");
}

查了一些資料也不太清楚rep;nop的作用,我想問的是,如果這里就是簡單的寫一個死循環(huán)會怎樣,即:
while (lock != 1);

請大牛指點!謝謝
作者: dingyujie    時間: 2012-12-09 13:06
“如果用一段死循環(huán)來代替這個循環(huán)是不行的,那樣會鎖住總線,unlock想設(shè)置值都不能了。rep;nop就是要讓CPU休息一下,把總線暫時讓出來!,看到別人有這種解釋,對嗎?
作者: firkraag    時間: 2012-12-09 13:43
看這個:
http://www.groad.net/bbs/read.php?tid-3373.html
作者: dingyujie    時間: 2012-12-09 15:27
本帖最后由 dingyujie 于 2012-12-09 15:28 編輯

謝謝了,文章講的很好!不過我的疑問是,如果不加這個指令,直接使用一個while循環(huán)會怎樣呢?會像我在2樓上說的那樣嗎回復(fù) 3# firkraag


   
作者: hk2305621    時間: 2012-12-09 15:51
3樓給的鏈接中說到了. nop 是編譯為 pause, 也就是讓CPU以極低的速率和功耗在運(yùn)行. 如果不加 rep;nop, cpu是以極高的速率運(yùn)行的,也就耗電.

而且,好像還有其他的目的, 是為了規(guī)避性能損失吧.

鏈接中有提到如下:
提升 spin-wait loops(自旋鎖循環(huán)等待)的性能。在執(zhí)行一個 spin-wait loop 時,Pentium4 處理器會
遇到嚴(yán)重的性能損失.PAUSE 指令會向處理器提供一種提示:告訴處理器所執(zhí)行的代碼序列是一個 spin-wait loop。
處理器會根據(jù)這個提示而避開內(nèi)存序列沖突(memory order violation),也就是說對 spin-wait loop 不做緩存,不做指令
重新排序等動作。這樣就可以大大的提高了處理器的性能。正是基于此,才建議在 spin-wait loops 中使用 pasuse 指令。

PAUSE指令的另外一個功能是讓 Pentium4 處理器在執(zhí)行 spin-wait loop 時可以減少電源的消耗。
在等待資源而執(zhí)行自旋鎖等待時,Pentium4 處理器以極快的速度執(zhí)行自旋等待時,將會消耗很多電能,
但使用 pause 指令則可以極大的減少處理器的電能消耗。  
作者: hk2305621    時間: 2012-12-09 15:51
@firkraag 學(xué)習(xí)了,謝謝你的鏈接.
作者: titer1    時間: 2012-12-09 20:20
一個忙等,有功耗考慮,有優(yōu)化,不簡單
作者: liuiang    時間: 2012-12-09 20:52
pasuse 另外一個作用是,超線程一個線程pasuse 會將處理器讓給另外一個線程,避免持有鎖的線程挨餓。
作者: leslielg    時間: 2012-12-10 08:51
我覺得這句話還有這樣的作用,讓寄存器的值都失效,而要cpu重新從memory加載變量(lock)的值。否則如果一直從寄存器讀取lock的值,那這就是個死循環(huán)了。
作者: dingyujie    時間: 2012-12-10 10:37
有道理!  回復(fù) 9# leslielg


   
作者: bensenq    時間: 2012-12-10 22:38
1. 編譯器的不會把這樣編譯吧?!
2. CPU會保證Cache一致性吧?!
leslielg 發(fā)表于 2012-12-10 08:51
我覺得這句話還有這樣的作用,讓寄存器的值都失效,而要cpu重新從memory加載變量(lock)的值。否則如果一直從 ...

作者: leslielg    時間: 2012-12-11 09:10
回復(fù) 11# bensenq


    1. 編譯器確實這樣編譯,名曰優(yōu)化,volatile關(guān)鍵字就這么來的。
    2. 這不是cache一致性,別弄混了。
作者: bensenq    時間: 2012-12-11 23:31
本帖最后由 bensenq 于 2012-12-12 00:01 編輯

回復(fù) 12# leslielg

你認(rèn)為那樣寫的原因是處于邏輯正確性考慮么?我完全不贊同,我認(rèn)為僅僅是性能優(yōu)化考慮,就像上面幾個帖子描述的那樣。

你說“否則如果一直從寄存器讀取lock的值,那這就是個死循環(huán)了。”我認(rèn)為這個理解是完全錯誤的.
while (lock != 1); 這句代碼的語義是:只要lock的值不等于1就繼續(xù)檢查lock的值,直到判斷到lock為1再執(zhí)行后面的指令。lock變量當(dāng)然是在內(nèi)存里,任何一個可用編譯器都會按照該語義要求CPU循環(huán)從內(nèi)存里讀取lock值,而不是所謂的"一直從寄存器讀取lock的值"。

直接上實驗吧:

test.c
  1. int lock;
  2. int main()
  3. {
  4.     while(lock != 1);
  5.     return 0;
  6. }
復(fù)制代碼
編譯,反匯編為test.s

gcc test.c
objdump a.out -d > test.s

直接跳到main函數(shù):
  1. 080483b4 <main>:
  2. 80483b4:   55                      push   %ebp
  3. 80483b5:   89 e5                   mov    %esp,%ebp
  4. 80483b7:   90                      nop
  5. 80483b8:   a1 18 a0 04 08          mov    0x804a018,%eax
  6. 80483bd:   83 f8 01                cmp    $0x1,%eax
  7. 80483c0:   75 f6                   jne    80483b8 <main+0x4>
  8. 80483c2:   b8 00 00 00 00          mov    $0x0,%eax
  9. 80483c7:   5d                      pop    %ebp
  10. 80483c8:   c3                      ret
復(fù)制代碼
可以發(fā)現(xiàn)while循環(huán)對應(yīng)80483b8~80483c0處的3條指令,分別讀取lock值(訪存)、比值較和跳轉(zhuǎn)功能,是不是沒一次循環(huán)都重新讀取lock的值呢?到這里我想結(jié)論已經(jīng)非常明顯了:程序不會一直從寄存器讀取值,因為這違背了這條C代碼的語義。當(dāng)然CPU每次讀取lock的值也不一定都會訪Memory,因為CPU和Memory之間還有Cache,只要Cache命中就會直接從Cache中取以節(jié)省時間,這也是我為什么提Cache一致性的原因。

回過頭來我們在來驗證一下樓主的疑問,看看內(nèi)嵌匯編的寫法到底帶來了那些優(yōu)化。

test1.c
  1. int lock;
  2. int main()
  3. {

  4.     while (lock != 1) {
  5.             __asm__ __volatile__("rep;nop": : :"memory");
  6.     }
  7.     return 0;
  8. }
復(fù)制代碼
編譯,反匯編為test1.s
gcc test1.c
objdump a.out -d > test1.s

main函數(shù):
  1. 080483b4 <main>:
  2. 80483b4:   55                      push   %ebp
  3. 80483b5:   89 e5                   mov    %esp,%ebp
  4. 80483b7:   eb 02                   jmp    80483bb <main+0x7>
  5. 80483b9:   f3 90                   pause
  6. 80483bb:   a1 18 a0 04 08          mov    0x804a018,%eax
  7. 80483c0:   83 f8 01                cmp    $0x1,%eax
  8. 80483c3:   75 f4                   jne    80483b9 <main+0x5>
  9. 80483c5:   b8 00 00 00 00          mov    $0x0,%eax
  10. 80483ca:   5d                      pop    %ebp
  11. 80483cb:   c3                      ret
復(fù)制代碼
while循環(huán)對應(yīng)指令為80483b9~80483c3 4條指令,發(fā)現(xiàn)區(qū)別了吧?其實就是在每次讀lock值前多了個pause指令(第一次除外,見80483b7處jmp)。關(guān)于pause的用途,樓上好幾位都說過了,主要影響多核、多線程下的CPU性能及功耗,和CPU體系結(jié)構(gòu)很密切,我也小google了一下,stackoverflow上有個回答供大家參看:How does x86 pause instruction work in spinlock *and* can it be used in other scenarios?
作者: bensenq    時間: 2012-12-11 23:42
本帖最后由 bensenq 于 2012-12-12 00:00 編輯

回復(fù) 12# leslielg
當(dāng)然,volatile關(guān)鍵字是使用來告訴編譯器:不要"隨便"幫我優(yōu)化這段代碼,否則“可能”會造成邏輯錯誤。

例如如果用 -O 選項優(yōu)化test.c程序,會發(fā)現(xiàn)其指令會有所改變(不影響本例子的結(jié)果)。

gcc -O1 test.c
objdump a.out -d > test.s
  1. 080483b4 <main>:
  2. 80483b4:   83 3d 18 a0 04 08 01    cmpl   $0x1,0x804a018
  3. 80483bb:   75 06                   jne    80483c3 <main+0xf>
  4. 80483bd:   b8 00 00 00 00          mov    $0x0,%eax
  5. 80483c2:   c3                      ret
  6. 80483c3:   eb fe                   jmp    80483c3 <main+0xf>
復(fù)制代碼
代碼被優(yōu)化為只讀一次lock值,如果不等于1就直接死循環(huán): jmp    80483c3 <main+0xf>。
   
作者: leslielg    時間: 2012-12-12 08:42
回復(fù) 13# bensenq


    no,你說的不對,volatile如果修飾變量,就是為了防止編譯器優(yōu)化為從寄存器取值而必須要每次都從內(nèi)存取值。一個變量不是總是從內(nèi)存取值。如果都是從內(nèi)存取值,那volatile修飾變量的用法就沒必要存在了。你先研究volatile的用法和含義吧,這個討論的基礎(chǔ)差別有點遠(yuǎn)了。
作者: leslielg    時間: 2012-12-12 09:08
你舉的例子我試了,恰好說明了我的推測是正確的:

int lock;

int main()
{
    while (lock != 1);
    return 0;
}

gcc -O2 test.c,反匯編后是:

08048300 <main>:
8048300:        83 3d 18 a0 04 08 01         cmpl   $0x1,0x804a018
8048307:        75 03                        jne    804830c <main+0xc>
8048309:        31 c0                        xor    %eax,%eax
804830b:        c3                           ret   
804830c:        eb fe                        jmp    804830c <main+0xc>
804830e:        90                           nop
804830f:        90                           nop

說明被優(yōu)化為只讀一次,并且編譯器自己做了邏輯判斷認(rèn)為是死循環(huán)了。

int lock;

int main()
{
    while (lock != 1)
       __asm__ __volatile__("rep;nop":::"memory");
    return 0;
}

gcc -O2 test.c
反匯編后:

08048300 <main>:
8048300:        eb 08                        jmp    804830a <main+0xa>
8048302:        8d b6 00 00 00 00            lea    0x0(%esi),%esi
8048308:        f3 90                        pause  
804830a:        83 3d 18 a0 04 08 01         cmpl   $0x1,0x804a018
8048311:        75 f5                        jne    8048308 <main+0x8>
8048313:        31 c0                        xor    %eax,%eax
8048315:        c3                           ret   
8048316:        90                           nop
8048317:        90                           nop

這樣就每次從內(nèi)存取值,并且邏輯上也正確了。

這就是我想說的:::memory在這里的作用,除了你們說的性能優(yōu)化,最重要的是,加上這句才能讓這個代碼運(yùn)行上正確。

作者: liuiang    時間: 2012-12-12 09:39
本帖最后由 liuiang 于 2012-12-14 10:08 編輯

volatile和__asm__ __volatile__("rep;nop":::"memory")都是謝絕編譯器針對變量的寄存器優(yōu)化的方法,

他們都是僅對生成的匯編指令有影響,而與體系結(jié)構(gòu)無關(guān)(編輯:注1),不會引入特殊指令,換句話說指令本身不會關(guān)注是訪問cache還是memory還是

外部寄存器(編輯:注2)或者是通過pci訪問其設(shè)備的任何空間。

前者可以保證所有對volatile修飾的變量的訪問,編譯器都會產(chǎn)生一次load或者store指令。后者貌似限定更強(qiáng)一些,似乎是清空/復(fù)位整個寄存器分配(后者是我的猜測,有錯請指正)。

注1:編輯:這里描述不正確,事實上改成“微體系”也是不對的,原本意思是,編譯器不會生成諸如“屏障”“cache刷新,無效”等額外特殊指令

注2:編輯:原帖這里是“寄存器”,描述的不準(zhǔn)確,實際上指的是內(nèi)存映射外部設(shè)備寄存器,而非處理器內(nèi)部通用寄存器

編輯:volatile在VS2005以后版本和Java中已經(jīng)隱含帶有了屏障語義(我沒驗證過,感興趣可以研究研究),這里不再展開更細(xì)節(jié)的討論。
作者: bensenq    時間: 2012-12-12 10:08
本帖最后由 bensenq 于 2012-12-12 10:09 編輯

回復(fù) 16# leslielg


1. 首先如果只說x86下生成的pause指令,毫無疑問是優(yōu)化作用,見上面13樓;
2. volatile的話,就是用來抑制優(yōu)化的,是和編譯器的優(yōu)化功能緊密相關(guān)的。你看在test.c(沒有內(nèi)嵌匯編)中,如果不加-O2選項下生成的代碼也是完全符合預(yù)期的(每次都從內(nèi)存中加載)。關(guān)于volatile的作用我在14樓已經(jīng)說的很明白,我不明白跟你說的“這個討論的基礎(chǔ)差別有點遠(yuǎn)了”是什么意思?好吧,那我就自己不補(bǔ)課吧,C Programming Language關(guān)于volatile關(guān)鍵字大概有幾點說明,如果還有理解的不到的地方的請你告訴我。

i. declaring it volatile announces that it has special properties relevant to optimization.
ii. The purpose of volatile is to force an implementation to suppress optimization that could otherwise occur.


3. 即便是加-O2后的優(yōu)化,也僅僅是只讀一次內(nèi)存就死循環(huán)了,而不會重復(fù)的從寄存器判斷值。這和你所說的“一直從寄存器讀取lock的值”是完全矛盾的,不知道你是不是和register關(guān)鍵字搞混了。
作者: bensenq    時間: 2012-12-12 10:11
回復(fù) 17# liuiang

匯編指令<-->機(jī)器指令<-->CPU指令。。。。。。。不就是體系結(jié)構(gòu)相關(guān)嘛?沒見過體系結(jié)構(gòu)無關(guān)的內(nèi)嵌匯編哦...

   
作者: liuiang    時間: 2012-12-12 11:20
回復(fù) 19# bensenq


    不好意思,搞錯了,是微體系。
作者: cengku    時間: 2012-12-14 09:07
mark,高手如云,問題分析得都很深刻。個人贊同liuiang 的分析。bensenq 的分析也很精彩。
作者: dingyujie    時間: 2012-12-14 23:35
非常感謝您的分析,學(xué)習(xí)了回復(fù) 13# bensenq


   
作者: leslielg    時間: 2012-12-17 14:41
回復(fù) 21# cengku


    他對volatile的理解是不對的,怎么這么基礎(chǔ)的問題這么多人都不知道?編譯器對一個變量會做什么樣的優(yōu)化而volatile會阻止什么樣的優(yōu)化不是個很基本的知識?不要被錯誤的認(rèn)識給誤導(dǎo)。
    另外這個問題的結(jié)論是必須加上這一句才能阻止編譯器的優(yōu)化,程序運(yùn)行才正確。性能優(yōu)不優(yōu)化不清楚,可能跟體系架構(gòu)有關(guān),即使有,也只是個附帶的作用。
作者: allen303allen    時間: 2012-12-17 20:53
leslielg 發(fā)表于 2012-12-17 14:41
回復(fù) 21# cengku

他對volatile的理解是不對的,怎么這么基礎(chǔ)的問題這么多人都不知道?編譯器對一個變量會做什么樣的優(yōu)化而volatile會阻止什么樣的優(yōu)化不是個很基本的知識?不要被錯誤的認(rèn)識給誤導(dǎo)。
    另外這個問題的結(jié)論是必須加上這一句才能阻止編譯器的優(yōu)化,程序運(yùn)行才正確。性能優(yōu)不優(yōu)化不清楚,可能跟體系架構(gòu)有關(guān),即使有,也只是個附帶的作用。


我想大家對volatile的理解并不像你想象的那樣,大家應(yīng)該都理解volatile的含義和作用,您再看看是不是自己搞錯了?

1. 樓主的問題是rep:nop的作用,所以前面幾樓的回答都沒什么大問題,其等價的pause簡單的說就是對cpu能耗的優(yōu)化,但你確糾結(jié)在__asm__ __volatile__上了。

2. __asm__ __volatile__中的volatile是用來修飾后面的內(nèi)嵌匯編的, 告訴編譯器不要隨便優(yōu)化我后面的匯編代碼,否則可能會有邏輯錯誤,這里跟lock變量沒有任何關(guān)系。另外,因為這里的內(nèi)嵌匯編非常簡單,根本沒有什么可以優(yōu)化的,所以這里用__asm__ __volatile__和只用__asm__編譯出的匯編代碼沒有任何區(qū)別。(加不加-O2都一樣)

3. 造成上面說的在循環(huán)體內(nèi)加  __asm__ __volatile__("rep;nop":::"memory"; 跟不加這條語句造成加優(yōu)化編譯后邏輯上正確與錯誤的區(qū)別,原因是是匯編指令中的"memory",memory強(qiáng)制gcc編譯器假設(shè)RAM所有內(nèi)存單元均被匯編指令修改,這樣cpu中的registers和cache中已緩存的內(nèi)存單元中的數(shù)據(jù)將作廢,cpu將不得不在需要的時候重新讀取內(nèi)存中的數(shù)據(jù)。
簡單的驗證方法是將這條語句改成 __asm__ __volatile__("rep;nop"::; 加優(yōu)化編譯后依然有邏輯錯誤。

4. 說到這里你應(yīng)該已經(jīng)明白了,要達(dá)到你說的目的,直接將lock變量的定義前加volatile就可以了。循環(huán)體內(nèi)的內(nèi)嵌匯編真正的作用是優(yōu)化用的,不過其中的"memory"恰好解決了-O2編譯時可能導(dǎo)致的邏輯錯誤問題,跟volatile是沒有關(guān)系的。
作者: bensenq    時間: 2012-12-17 21:14
回復(fù) 24# allen303allen

跟我理解的一樣,并且補(bǔ)充了一些我不知道的東西,感謝。
   
作者: liuiang    時間: 2012-12-17 21:15
我的理解跟leslielg 應(yīng)該是一樣的,并大部分同意樓上。leslielg 說的volatile與代碼的__volatile__并不是一回事。
作者: onlyxuyang    時間: 2012-12-17 23:05
intel的架構(gòu)還在用鎖總線的辦法來做spin lock么?。。。怪不得能耗大。。。

arm用的strex和ldrex已經(jīng)可以不需要鎖總線了,顯式的wfi指令主動進(jìn)入低能耗loop。
作者: dingyujie    時間: 2012-12-18 15:57
是的回復(fù) 27# onlyxuyang


   
作者: cengku    時間: 2012-12-20 09:25
volatile的理解大家應(yīng)該都是一致的,volatile跟性能沒有半分錢的關(guān)系。
回復(fù) 23# leslielg


   
作者: cengku    時間: 2012-12-20 09:37
黃總的分析很清晰,說到了問題的本質(zhì),學(xué)習(xí)了!
allen303allen 發(fā)表于 2012-12-17 20:53
我想大家對volatile的理解并不像你想象的那樣,大家應(yīng)該都理解volatile的含義和作用,您再看看是不是自 ...

作者: daniel_11    時間: 2012-12-20 12:04
回復(fù) 24# allen303allen
講的非常清楚,學(xué)習(xí)了。這里顯然不是一個violate變量問題。


   
作者: leslielg    時間: 2012-12-26 15:35
回復(fù) 24# allen303allen


    我說的是volatile,不是這里的這句__volatile__,我提volatile是用來跟bensenq類比為何這里要加上:::memory的,因為這里的lock不是volatile的變量(它不能被加上volatile,大部分時間里會影響性能),所以它會被優(yōu)化為從寄存器取值,所以這里需要這樣一句匯編用:::memory來達(dá)到volatile的效果。這是編譯器對非volatile變量的優(yōu)化,而內(nèi)核必須用-O2來編譯,所以這里肯定會優(yōu)化,編譯器對變量的優(yōu)化就是從寄存器取值而不從內(nèi)存取值,至于這里的死循環(huán)是進(jìn)一步邏輯優(yōu)化的結(jié)果。而bensenq 否認(rèn)了編譯器會對變量優(yōu)化為從寄存器取值,所以我才提到了volatile,但是恰好這里又有個__volatile__,所以我看大家理解上可能有誤差了?

性能優(yōu)化我確實不清楚,但我覺得應(yīng)該跟體系架構(gòu)有關(guān),也是gcc針對不同體系架構(gòu)做出的編譯結(jié)果,因為并不是所有cpu在nop時都有這樣的節(jié)能指令,所以這里不一定是寫這個代碼的人的意圖?僅是猜測。

所以我仍然認(rèn)為這句話最主要的作用是在用:::memory來防止因為編譯器優(yōu)化而產(chǎn)生的邏輯錯誤的。
作者: leslielg    時間: 2012-12-26 15:40
回復(fù) 26# liuiang


    我明白你明白了。
作者: leslielg    時間: 2012-12-26 16:12
不知可否做這樣一個歸納:

1. 理論上講所有并發(fā)訪問的全局變量都需要加volatile
2. 但是對頻繁訪問的全局變量用volatile會較大的影響性能
3. 所以可以對并發(fā)全局變量不加volatile,而在代碼有并發(fā)訪問的地方,用:::memory這樣的匯編來起到一樣的效果

這里的語句就是這樣一個例子
作者: leslielg    時間: 2012-12-26 16:44
另外還有一個細(xì)節(jié),:::memory我所知道的是讓寄存器里緩存的變量都失效,而完全不會影響到cpu cache。樓上有人提到這條語句對cpu cache的影響,不知從何而來??影響是什么?所有的cache line全部失效??麻煩能否提供以下出處。

據(jù)我所知,cpu cache要失效軟件只有通過對協(xié)處理器的操作指令才可以做到。所以我看到bensenq提到cache一致性覺得完全是不沾邊的。
作者: liuiang    時間: 2012-12-27 04:22
:::memory是編譯器復(fù)位寄存器分配。volatile只是針對其所修飾的變量。沒搞過編譯器,不過聽上去倒像是:::memory的力度更強(qiáng)。




歡迎光臨 Chinaunix (http://72891.cn/) Powered by Discuz! X3.2