mutex和rwmutex類型基本概念
pthread_mutex_t:互斥鎖,同一瞬間只能有一個線程能夠獲取鎖,其他線程在等待獲取鎖的時候會進入休眠狀態(tài)。因此pthread_mutex_t消耗的CPU資源很小,但是性能不高,因為會引起線程切換。
pthread_spinlock_t:自旋鎖,同一瞬間也只能有一個線程能夠獲取鎖,不同的是,其他線程在等待獲取鎖的過程中并不進入睡眠狀態(tài),而是在 CPU上進入“自旋”等待。自旋鎖的性能很高,但是只適合對很小的代碼段加鎖(或短期持有的鎖),自旋鎖對CPU的占用相對較高。
pthread_rwlock_t:讀寫鎖,同時可以有多個線程獲得讀鎖,同時只允許有一個線程獲得寫鎖。其他線程在等待鎖的時候同樣會進入睡眠。讀寫鎖在互斥鎖的基礎(chǔ)上,允許多個線程“讀”,通常在讀多寫少的場景下能提高性能。
std::mutex:互斥鎖,同一瞬間只能有一個線程能夠獲取鎖,等待獲取鎖的線程將會被阻塞并處于等待(waiting)狀態(tài)。
std::shared_mutex:共享讀寫鎖,支持多個讀線程同時訪問,但只允許一個寫線程訪問。std::shared_mutex實現(xiàn)讀寫分離,讀鎖需要跟蹤讀者數(shù)量,鎖定/解鎖開銷更大。寫操作仍然是獨占的,寫鎖性能與std::mutex相當(dāng)。
nsync_mutex_t:google開源并發(fā)庫nsync的互斥鎖。
nsync_rwlock_t:google開源并發(fā)庫nsync的讀寫鎖,即write mode下使用lock,read mode下使用rlock。
附nsync庫地址:GitHub - google/nsync: nsync is a C library that exports various synchronization primitives, such as mutexesnsync is a C library that exports various synchronization primitives, such as mutexes - GitHub - google/nsync: nsync is a C library that exports various synchronization primitives, such as mutexeshttps://github.com/google/nsync
?測試機器情況
測試結(jié)果
本次測試使用std::unorder_map。
exp.1
os: linux
threads: 3讀1寫
hashmap length: 36675
read times: 10000 / thread
write times: 10000 / thread
epoch: 4
加鎖方式 |
cost1 |
cost2 |
cost3 |
cost4 |
avg |
|
std::mutex |
批 |
9186 us |
6446 us |
6415 us |
6403 us |
7112 us |
pthread_mutex_t |
單 |
24062 us |
19120 us |
16866 us |
17141 us |
19297 us |
批 |
9730 us |
18748 us |
16933 us |
17394 us |
15701 us |
|
pthread_rwlock_t |
單 |
21546 us |
19722 us |
18900 us |
20660 us |
20207 us |
批 |
13019 us |
10260 us |
10149 us |
10330 us |
10939 us |
|
pthread_spinlock_t |
單 |
24909 us |
24051 us |
20031 us |
20241 us |
22308 us |
批 |
10846 us |
6420 us |
6395 us |
6393 us |
7513 us |
|
nsync_mutex_t |
單 |
29628 us |
28371 us |
25245 us |
25253 us |
27124 us |
批 |
9267 us |
6562 us |
6501 us |
6500 us |
7207 us |
|
nsync_rwlock_t (寫lock, 讀rlock mix) |
單 |
25121 us |
24177 us |
21694 us |
21674 us |
23166 us |
批 |
13293 us |
10319 us |
10439 us |
10589 us |
11160 us |
|
std::shared_mutex |
批 |
13134 us |
13138 us |
10487 us |
10671 us |
11857 us |
exp.2
os: linux
threads: 3讀1寫
hashmap length: 36675
read times: 10000 / thread
write times: 1000 / thread
epoch: 4
加鎖方式 |
cost1 |
cost2 |
cost3 |
cost4 |
avg |
|
std::mutex |
批 |
4965 us |
4612 us |
4630 us |
4646 us |
4713 us |
pthread_mutex_t |
單 |
12305 us |
9831 us |
10552 us |
10900 us |
10897 us |
批 |
4968 us |
9776 us |
10180 us |
10894 us |
8954 us |
|
pthread_rwlock_t |
單 |
13780 us |
13561 us |
13517 us |
13342 us |
13550 us |
批 |
8906 us |
8408 us |
8443 us |
8428 us |
8546 us |
|
pthread_spinlock_t |
單 |
13822 us |
12730 us |
14003 us |
13518 us |
13518 us |
批 |
5214 us |
4691 us |
4666 us |
4754 us |
4831 us |
|
nsync_mutex_t |
單 |
18121 us |
17780 us |
18220 us |
18829 us |
18237 us |
批 |
4699 us |
4534 us |
4496 us |
4675 us |
4601 us |
|
nsync_rwlock_t (寫lock, 讀rlock mix) |
單 |
16456 us |
16375 us |
15957 us |
16975 us |
16440 us |
批 |
8821 us |
8800 us |
8592 us |
8473 us |
8671 us |
|
std::shared_mutex |
批 |
8891 us |
8565 us |
8748 us |
8559 us |
8690 us |
exp.3
os: linux
threads: 4讀2寫
hashmap length: 36675
read times: 10000 / thread
write times: 1000 / thread
epoch: 4
加鎖方式 |
cost1 |
cost2 |
cost3 |
cost4 |
avg |
|
std::mutex |
批 |
7697 us |
6348 us |
6094 us |
6086 us |
6556 us |
pthread_mutex_t |
單 |
21670 us |
18028 us |
18288 us |
18539 us |
19131 us |
批 |
6684 us |
18915 us |
18855 us |
18791 us |
15811 us |
|
pthread_rwlock_t |
單 |
20989 us |
20792 us |
20321 us |
20618 us |
20680 us |
批 |
16854 us |
16228 us |
15962 us |
16417 us |
16365 us |
|
pthread_spinlock_t |
單 |
21585 us |
22893 us |
21294 us |
22650 us |
22105 us |
批 |
7055 us |
7292 us |
6142 us |
7366 us |
6963 us |
|
nsync_mutex_t |
單 |
24586 us |
24548 us |
24948 us |
25607 us |
24922 us |
批 |
6602 us |
6008 us |
5980 us |
5985 us |
6143 us |
|
nsync_rwlock_t (寫lock, 讀rlock mix) |
單 |
22541 us |
23444 us |
22741 us |
22859 us |
22896 us |
批 |
17076 us |
15687 us |
16011 us |
15786 us |
16140 us |
|
std::shared_mutex |
批 |
16847 us |
16008 us |
15922 us |
16075 us |
16213 us |
exp.4
os: linux
threads: 6讀3寫
hashmap length: 36675
read times: 10000 / thread
write times: 3000 / thread
epoch: 4
加鎖方式 |
cost1 |
cost2 |
cost3 |
cost4 |
avg |
|
std::mutex |
批 |
13420 us |
10872 us |
10700 us |
10704 us |
11424 us |
pthread_mutex_t |
單 |
43406 us |
41285 us |
40320 us |
40768 us |
41444 us |
批 |
12956 us |
40143 us |
38579 us |
38886 us |
32641 us |
|
pthread_rwlock_t |
單 |
42786 us |
38810 us |
39713 us |
38104 us |
39853 us |
批 |
31753 us |
29010 us |
29338 us |
29371 us |
29868 us |
|
pthread_spinlock_t |
單 |
49577 us |
44320 us |
42921 us |
39853 us |
44167 us |
批 |
16009 us |
12867 us |
15400 us |
10944 us |
13805 us |
|
nsync_mutex_t |
單 |
48348 us |
47371 us |
44357 us |
44867 us |
46235 us |
批 |
13579 us |
10974 us |
10890 us |
10833 us |
11569 us |
|
nsync_rwlock_t (寫lock, 讀rlock mix) |
單 |
46518 us |
46523 us |
45031 us |
44671 us |
45685 us |
批 |
32345 us |
29480 us |
29459 us |
29425 us |
30177 us |
|
std::shared_mutex |
批 |
31113 us |
28986 us |
28955 us |
31712 us |
30191 us |
exp.5
os: linux
threads: 8讀4寫
hashmap length: 36675
read times: 10000 / thread
write times: 3000 / thread
epoch: 4
加鎖方式 |
cost1 |
cost2 |
cost3 |
cost4 |
avg |
|
std::mutex |
批 |
18684 us |
15223 us |
15186 us |
15191 us |
16071 us |
pthread_mutex_t |
單 |
61733 us |
55128 us |
52081 us |
52337 us |
55319 us |
批 |
18807 us |
53878 us |
51183 us |
50275 us |
43535 us |
|
pthread_rwlock_t |
單 |
58043 us |
55728 us |
54218 us |
55093 us |
55770 us |
批 |
47133 us |
44446 us |
47107 us |
43977 us |
45665 us |
|
pthread_spinlock_t |
單 |
73558 us |
65183 us |
58573 us |
61395 us |
64677 us |
批 |
20976 us |
20723 us |
17269 us |
16682 us |
18542 us |
|
nsync_mutex_t |
單 |
64249 us |
61961 us |
59575 us |
60961 us |
61686 us |
批 |
19168 us |
15756 us |
15542 us |
15591 us |
16514 us |
|
nsync_rwlock_t (寫lock, 讀rlock mix) |
單 |
66496 us |
63230 us |
59097 us |
60990 us |
62453 us |
批 |
48564 us |
45194 us |
45920 us |
45330 us |
46252 us |
|
std::shared_mutex |
批 |
47652 us |
44839 us |
44833 us |
44265 us |
45397 us |
實驗結(jié)論?
1.按照keys的批次加鎖的情況來看,pthread_mutex_t是性能最差的(剔除異常的cost1數(shù)據(jù)),其次由于共享資源的鎖競爭較小,pthread_rwlock_t和std::shared_mutex相差不大,表現(xiàn)最好的是nsync_mutex_t。就目前的實驗參數(shù)來看,std::mutex和nsync_mutex_t還要略優(yōu)于pthread_spinlock_t。nsync_rwlock_t、std::shared_mutex和pthread_rwlock_t在批量加鎖的性能表現(xiàn)上是十分相近的。建議在一般情況下,使用std::mutex即可,盡量不用讀寫鎖。
2.pthread_mutex_t、pthread_rwlock_t、pthread_spinlock_t、nsync_mutex_t、nsync_rwlock_t在對單key加鎖時性能普遍較低,不推薦在單key的粒度下使用。
3.當(dāng)共享資源的鎖競爭很小時,rwlock的cpu性能消耗反而比spinlock要高。所以不是所有讀多寫少的情況下,rwlock都優(yōu)于spinlock。
3.1分析:從spinlock和rwlock的功能上分析,前者只允許一個線程訪問共享資源,其余線程均是忙等待;后者則允許多個讀者同時訪問,絕對限制只有一個線程可以作為寫者。當(dāng)讀者訪問資源時,寫者必須忙等待,反之亦然。只從功能描述上分析,rwlock從功能上應(yīng)該比spinlock性能要好。而cache數(shù)據(jù)結(jié)構(gòu)一般使用hash表維護,當(dāng)散列函數(shù)比較理想時,鎖競爭發(fā)生的概率可能很小。由此猜測rwlock造成的性能下降,可能是因為rwlock的自身上鎖解鎖的cpu消耗要比spinlock高導(dǎo)致的。在上鎖時,pthread_rwlock_wrlock和pthread_rwlock_rdlock會引入線程阻塞和切換的開銷,而pthread_spin_lock會自旋等待鎖,不會阻塞線程,減少了線程切換的開銷。
4.在pthread系列中,讀寫鎖的性能要優(yōu)于互斥鎖;但在nsync系列中,互斥鎖要優(yōu)于讀寫鎖。
5.std::mutex在linux環(huán)境下性能比pthread_mutex_t要好的原因有以下幾點:
-
std::mutex采用自旋鎖+條件變量實現(xiàn),在鎖持有時間短的情況下可以避免上下文切換。而pthread_mutex_t直接使用操作系統(tǒng)mutex,依賴系統(tǒng)調(diào)用獲取鎖,上下文切換開銷較大。
解釋:GCC標(biāo)準(zhǔn)庫實現(xiàn)的std::mutex,在Linux下默認會使用PTHREAD_MUTEX_ADAPTIVE_NP類型的pthread mutex作為底層實現(xiàn)。這種底層實現(xiàn)可以讓std::mutex在不同的并發(fā)場景下自動選擇最優(yōu)的實現(xiàn)策略(鎖競爭少時采用自旋,多時采用阻塞實現(xiàn)來避免頻繁上下文切換)。而pthread_mutex_t是固定使用一種類型(阻塞),因此這是std::mutex在linux環(huán)境下比pthread_mutex_t更優(yōu)的原因。
-
C++標(biāo)準(zhǔn)庫mutex實現(xiàn)經(jīng)過了大量優(yōu)化,如GCC里采用了特定于CPU的優(yōu)化指令。文章來源:http://www.zghlxwxcb.cn/news/detail-782070.html
-
std::mutex可以實現(xiàn)鎖粒度更細的優(yōu)化控制,避免不必要的鎖競爭。文章來源地址http://www.zghlxwxcb.cn/news/detail-782070.html
到了這里,關(guān)于C++ 各類mutex和讀寫鎖性能比較的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!