無鎖編程與有鎖編程的效能對比與分析

來源:互聯網
上載者:User

 

      最近維護的一個網路伺服器遇到效能問題,於是就對原有的程式進行了較大的架構改動。改動最多的是線程工作模式與資料傳遞方式,最終的結果是改變鎖的使用模式。經過一番改進,基本上可以做到

GMb

網卡全速工作處理。在

效能達標之後,一度在想有沒有什麼辦法使用更加輕量級鎖,或者去掉鎖的使用,為此搜尋一些相關的研究成果,並做了一些實驗來驗證這些成果,因而就有這篇文章。希望有做類似工作的同行可以有所借鑒。如果有人也有相關的經驗,歡迎和我交流。

 

1

無鎖編程概述

      本節主要對文獻

[1]

進行概括,做一些基礎知識的介紹。

      所謂有鎖編程,就是當你需要共用資料的時候,你需要有序的去訪問,所有改變共用資料的操作都必須表現出原子的語義,即便是像

++k

,這種操作也需要使用鎖進行。有鎖編程面臨時效率的下降、死結、優先順序反轉等問題,都需要設計者小心的進行最佳化和解決。本文並不對這三個問題進行討論。

      在無鎖編程中,並不是說所有操作都是原子的,只有一個很有限的操作集是原子的,這就意味著無鎖編程十分困難。那麼這個有限的操作集是否存在,存在的話包含哪些原子操作呢?

2003

Maurice Herlihy

的一篇論文

”Wait-Free Synchronization”[3]

解決了這個問題。這裡給出文章的結論,文章指出像

test-and-set,swap,fetch-and-add

甚至是原子隊列對於多線程而言都無法做到

lock-free

。而最樸素最簡單的原語

CAS(compare-and-swap)

操作即可以完成所有的無鎖功能,其他的如

LL/SC (load linked/store
conditional)

CAS

的偽碼如下:

template <class T><br />bool CAS(T* addr, T expected, T value)<br />{<br /> if (*addr == expected)<br /> {<br /> *addr = value;<br /> return true;<br /> }<br /> return false;<br />}<br />

 

      CAS

expected

與一個記憶體位址進行比較,如果比較成功,就將記憶體內容替換為

new

。當前大多數機器都在硬體級實現了這個操作,在

Inter

處理器上這個操作是

CMPXCHG

,因而

CAS

是一個最基礎的原子操作。

      wait-free / lock-free


有鎖對比

      wait-free

的過程可以通過有限步驟完成,而不管其他線程的速度。

      lock-free

的過程保證至少一個線程在執行,其他線程可能會被延遲,但系統整體仍在前進。

      有鎖的情況下,如果某個線程佔有鎖,則其他線程就無法執行。更普通的,有鎖需要避免死結和活鎖的情況。

 

2

無鎖編程的相關研究與進展

 

      本節內容對文獻

[2]

進行概述,介紹當前已經實現的無鎖演算法與資料結構。

        近二十年來研究者們對

lock-free

wait-free

的演算法和資料結構進行了大量的研究。實現了一些

wait-free

lock-free

的演算法,比如

FIFO

的隊列和

LIFO

的棧,而更複雜的最佳化級隊列、

hash

表及紅/黑樹狀結構的

lock-free

演算法也漸漸為人所知。

        無鎖演算法的實現都依賴記憶體屏障,因而具有平台相關性。下面將列舉目前已經較為成熟的原子操作和演算法資料結構的實現。

  • MidiShare Source
    Code

    is available under the GPL license. MidiShare includes
    implementations of lock-free FIFO queues and LIFO stacks.

  • Appcore

    is an SMP and HyperThread friendly library which uses
    Lock-free techniques to implement stacks, queues, linked lists and other
    useful data structures. Appcore appears currently to be for x86 computers
    running Windows. The licensing terms of Appcore are extremely unclear.

  • Noble

    – a library of
    non-blocking synchronisation protocols. Implements lock-free stack, queue,
    singly linked list, snapshots and registers. Noble is distributed under a
    license which only permits non-commercial academic use.

  • lock-free-lib

    published under the GPL license. Includes implementations of software
    transactional memory, multi-workd CAS primitives, skip lists, binary
    search trees, and red-black trees. For Alpha, Mips, ia64, x86, PPC, and
    Sparc.

  • Nonblocking
    multiprocessor/multithread algorithms in C++

    (for MSVC/x86) posted by
    Joshua Scholar to musicdsp.org

    , and are
    presumably in the public domain. Included are queue, stack,
    reference-counted garbage collection, memory allocation, templates for
    atomic algorithms and types. This code is largely untested. A local mirror
    is here

    .

  • Qprof

    includes the Atomic_ops library of atomic operations and data structures
    under an MIT-style license. Only available for Linux at the moment, but
    there are plans to support other platforms. download available here

  • Amino Concurrent
    Building Blocks

    provides lock free datastructures and STM for C++ and
    Java under an Apache Software (2.0) licence.

      其中

Noble

已經進行了商業化,

License

相當不便宜。

3

效能分析

      本節對

PTHREAD

中的

mutex

windows

中的原子增,及

CAS

原子操作進行對比,並對

MidiShare

中實現的無鎖

FIFO

隊列與基於

STL

list

實現的有鎖隊列進行的效能對比和分析,並對最佳化方式進行了總結。

3.1

原子增的效能測試

      測試機

CPU

Intel E5300 2.60GHZ

      首先是對簡單的遞增操作進行了測試,分別對無任何同步機制的

++

操作、

pthread_mutex

保護的

++

操作,以及

CAS

的語義實現的

atomic_add1()

以及

windows

下的

interlockedIncrease()

進行了單個線程情況下的定量測試。

 

i++

3.2

lock(p_mutex);i++;unlock(p_mutex);

2

千萬

CAS_atomic_add1(i)

4

千萬

interlockedIncrease(&i)

4

千萬

 

     首先在無任何同步情況下,

CPU

可以每秒執行

++

操作

3.2

億次,接近於

CPU

的主頻速率。而每次

++

時執行

thread_mutex_lock()

unlock()

操作情況下,

CPU

每秒只能執行

2

千萬次,這就是說

CPU

每秒鐘可以執行加鎖及解鎖操作共

4

千萬次,加解鎖的開銷是執行加法指令的的

15

倍左右。而

CAS

的情況稍好,為每秒

4

千萬次。這個速度與

windows

下的

interlockedIncrease()

的執行速度十分近似。

      從上面的測試結果來看,

windows

下的原子增操作與

CAS

實現的增操作代價基本是相同的,估計

windows

底層也是藉助彙編指令

CMPXCHG

CAS

來實現原子增操作的。當然

pthread_mutex

作為一種互斥鎖,也是擁有相當高的效率的,在沒有鎖突然的情況下,加鎖開銷與一次

CAS

的開銷相當。

      但如果對比無同步的

++

操作,硬體級的同步也造成了至少

8

倍的效能下降。

      接著,對

pthread_mutex

的程式進行了邏輯最佳化,分別測試了

++

執行

8

次、

20,100

次進行一次加解鎖的情況。

lock();for(k=0;k<8;i++,k++);unlock()

1.2

lock();for(k=0;k<20;i++,k++);unlock()

2

lock();for(k=0;k<100;i++,k++);unlock()

3.4

      結果

CPU

每秒鐘可以執行

++

的次數為

1.2

/2

/3.4

億,這種情況與預期是一致的,因為每秒鐘調用加解鎖的次數分別是原來的

1/8

1/20

1/100

,當執行

100

++

進行一次加解鎖後,效能已經達到了無任何同步時的效能。當然原子的

interlockedIncrease()

CAS

實現的

atomic_add1()

都不具備這種批量處理的改進優勢,無論如果,它們最好的執行情況已經固定了。

對於在單線程與多線程的情況下的

windows

下的原子操作的效能測試情況,可以參考文獻

[4]

,這裡只列出其中的結論。其所列的測試機

CPU

Intel2.66GHZ

雙核處理器。

      單個線程執行

2

百萬次原子增操作

interlockedIncrease

78ms

Windows
CriticalSection

172ms

OpenMP

lock

操作

250ms

      兩個線程對共用變數執行

2

百萬次原子增操作

interlockedIncrease

156ms

Windows
CriticalSection

3156ms

OpenMP

lock

操作

1063ms

3.2

無鎖隊列與有鎖隊列的效能測試

      這裡測試的無鎖列隊由

MidiShare

實現的,而有鎖隊列是通過

pthread_mutex

c++

STL list

共同實現。這裡只列出測試結果。

      對於儲存相同的資料的情況下,從主線程

enque

並從子線程

deque

,計算每秒鐘

enque/deque

的次數,當然二者基本上是相同的。

      無鎖隊列的效能在

150w -200w

次入隊操作,這個效能已經無法再有任何提高,因為每次入隊出隊操作都是硬體級的互斥。而對於有鎖隊列,根據每次加解鎖之間處理入隊的次數的不同,有以下的結果:

lock();for(k=0;k<x;i++,k++);unlock()

結果(次/s)

x=1

40

x=10

190

x=128

350

x=1000

400

x=10000

396

      這說明通過對鎖之間的資料進行批處理,可以極大的提高系統的效能,而使用原子操作,則無法實現批處理上的改進。

4

結論

      通過上面的無鎖和有鎖的效能測試,可以得出這樣的結論,對於

CAS

實現的硬體級的互斥,其單次操作效能比相同條件下的應用程式層的較為高效,但當多個線程並發時,硬體級的互斥引入的代價與應用程式層的鎖爭用同樣令人惋惜。因此如果純粹希望通過使用

CAS

無鎖演算法及相關資料結構而帶來程式效能的大量提升是不可能的,硬體級原子操作使應用程式層操作變慢,而且無法再度最佳化。相反通過對有鎖多線程程式的良好設計,可以使程式效能沒有任何下降,可以實現高度的並發性。

      但是我們也要看到應用程式層無鎖的好處,比如不需要程式員再去考慮死結、優先順序反轉等棘手的問題,因此在對應用程式不太複雜,而對效能要求稍高時,可以採用有鎖多線程。而程式較為複雜,效能要求滿足使用的情況下,可以使用應用級無鎖演算法。

 
   

至於如何對多線程的工作模式進行更好的調度,可以參考文獻

[5]

,文獻介紹了一種較好的線程間合作的工作模式,當然前提是機器的處理器個數較多,足以支援多組線程並行的工作。如果處理器個數較,較多的線程之間在各個核心上來回調度增加了系統環境切換的開銷,會導致系統整體效能下降。

 

參考文獻

[1] Lock-Free Data Structures

http://www.drdobbs.com/184401865

[2] Some notes on lock-free
wait-free algorithms http://www.rossbencina.com/code/lockfree

[3] Wait-Free Synchronization http://www.podc.org/dijkstra/2003.html

[4] OpenMP

建立線程中的鎖及原子操作效能比較

http://blog.163.com/kangtao-520/blog/static/772561452009510751068/

[5]

多核編程中的線程分組競爭模式

http://kangtao-520.blog.163.com/blog/static/77256145200951074121305/

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.