為了探究SMT對(duì)計(jì)算密集型workload的效果,我自己寫了一個(gè)簡(jiǎn)單的測(cè)試程序。
使用pthread開(kāi)多個(gè)線程,每個(gè)線程分別計(jì)算斐波那契數(shù)列第N號(hào)元素的值。每個(gè)線程計(jì)算斐波那契數(shù)列時(shí)除線程的元數(shù)據(jù)外只分配兩個(gè)unsigned long變量,由此避免過(guò)高的內(nèi)存開(kāi)銷。
workload的詳細(xì)代碼和測(cè)試腳本在[https://github.com/HongweiQin/smt_test]
毫無(wú)疑問(wèn),這是一個(gè)計(jì)算密集型負(fù)載,我在自己的筆記本上運(yùn)行,配置如下(省略了一些不重要的項(xiàng)目):
lscpu
Architecture: x86_64
12 :
CPU(s) list: 0-11
per core: 2
per socket: 6
1 :
NUMA node(s): 1
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
L1d cache: 192 KiB
L1i cache: 192 KiB
L2 cache: 1.5 MiB
L3 cache: 12 MiB
可以看到筆記本有一個(gè)Intel i7的處理器,6核12線程。經(jīng)查,CPU0和CPU6共用一個(gè)Core,CPU1和CPU7共用一個(gè)Core,以此類推。
以下的測(cè)試(Test 1-5)中,每個(gè)線程分別計(jì)算斐波那契數(shù)列第40億號(hào)元素的數(shù)值。
Test1:采用默認(rèn)配置,開(kāi)12線程進(jìn)行測(cè)試。測(cè)試結(jié)果為總耗時(shí)45.003s。
qhw@qhw-laptop:~/develop/smt_test$ time ./smt_test -f 4000000000
threads_num=12, fibonacci_max=4000000000, should_set_affinity=0, should_inline=1, alloc_granularity=32
real0m45.003s
user7m12.953s
sys0m0.485s
Test2:把smt關(guān)掉,同樣的測(cè)試方法(12線程)??偤臅r(shí)為25.733s。
qhw@qhw-laptop:~/develop/smt_test$ cat turnoff_smt.sh
echo "turn off smt"
sudo sh -c 'echo off > /sys/devices/system/cpu/smt/control'
qhw@qhw-laptop:~/develop/smt_test$ ./turnoff_smt.sh
turn off smt
qhw@qhw-laptop:~/develop/smt_test$ time ./smt_test -f 4000000000
threads_num=12, fibonacci_max=4000000000, should_set_affinity=0, should_inline=1, alloc_granularity=32
real0m25.733s
user2m23.525s
sys0m0.116s
對(duì),你沒(méi)看錯(cuò)。同樣的workload,如果關(guān)掉smt,總耗時(shí)還變少了。Intel誠(chéng)不欺我!
Test3:再次允許smt,但是將程序限制在三個(gè)物理Core上運(yùn)行,則總耗時(shí)為34.896s。
qhw@qhw-laptop:~/develop/smt_test$ ./turnon_smt.sh
turn on smt
qhw@qhw-laptop:~/develop/smt_test$ time taskset -c 0-2,6-8 ./smt_test -f 4000000000
threads_num=12, fibonacci_max=4000000000, should_set_affinity=0, should_inline=1, alloc_granularity=32
real0m34.896s
user3m17.033s
sys0m0.028s
Test3相比于Test1用了更少的Core,反而更快了。
為什么在Test2和3會(huì)出現(xiàn)這樣違反直覺(jué)的結(jié)果?
猜想:Cache一致性在作怪!
圖1
測(cè)試程序的main函數(shù)會(huì)分配一個(gè)含有T(T=nr_threads)個(gè)元素的`struct thread_info`類型的數(shù)組,并分別將每個(gè)元素作為參數(shù)傳遞給每個(gè)計(jì)算線程使用。`struct thread_info`定義如下:
struct thread_info {
pthread_t thread_id;
int thread_num;
unsigned long res[2];
};
結(jié)構(gòu)體中的res數(shù)組用于計(jì)算斐波那契數(shù)列,因此會(huì)被工作線程頻繁地寫。
注意到,sizeof(struct thread_info)為32,而我的CPU的cacheline大小為64B!這意味著什么?
如果Thread 0在Core 0上運(yùn)行,則它會(huì)頻繁寫tinfo[0],Thread 1在Core 1上運(yùn)行,則它會(huì)頻繁寫tinfo[1]。
這意味著,當(dāng)Thread 0寫tinfo[0]時(shí),它其實(shí)是寫入了Core 0上L1 Cache的Cacheline。同樣的,當(dāng)Thread 1寫tinfo[1]時(shí),它其實(shí)是寫入了Core 1上L1 Cache的Cacheline。此時(shí),由于Core 1上的Cacheline并非最新,因此CPU需要首先將Core 0中的Cacheline寫入多核共享的L3 Cache甚至是內(nèi)存中,然后再將其讀入Core 1的L1 Cache中,最后再將Thread 1的數(shù)據(jù)寫入。此時(shí),由于Cache 0中的數(shù)據(jù)并非最新,Cacheline會(huì)被無(wú)效化。由此可見(jiàn),如果程序一直這樣運(yùn)行下去,這一組數(shù)據(jù)需要在Cache 0和1之間反復(fù)跳躍,占用較多時(shí)間。
這個(gè)猜想同樣可以解釋為什么使用較少的CPU可以加速程序運(yùn)行。原因是當(dāng)使用較少的CPU時(shí),多線程不得不分時(shí)共用CPU,如果Thread 0和Thread 1分時(shí)共用了同一個(gè)CPU,則不需要頻繁將Cache無(wú)效化,程序運(yùn)行時(shí)間也就縮短了。
驗(yàn)證猜想:增加內(nèi)存分配粒度!
對(duì)程序進(jìn)行修改后,可以使用`-g alloc_granularity`參數(shù)設(shè)定tinfo結(jié)構(gòu)體的分配粒度。使用4KB為粒度進(jìn)行分配,再次進(jìn)行測(cè)試:
Test4:12線程,開(kāi)啟SMT,分配粒度為4096??偤臅r(shí)為13.193s,性能相比于Test1的45.003s有了質(zhì)的提升!
qhw@qhw-laptop:~/develop/smt_test$ time ./smt_test -f 4000000000 -g 4096
threads_num=12, fibonacci_max=4000000000, should_set_affinity=0, should_inline=1, alloc_granularity=4096
real0m13.193s
user2m31.091s
sys0m0.217s
Test5:在Test4的基礎(chǔ)上限制只能使用3個(gè)物理Core。總耗時(shí)為24.841s,基本上是Test4的兩倍。這說(shuō)明在這個(gè)測(cè)試下,多核性能還是線性可擴(kuò)展的。
qhw@qhw-laptop:~/develop/smt_test$ time taskset -c 0-2,6-8 ./smt_test -f 4000000000 -g 4096
threads_num=12, fibonacci_max=4000000000, should_set_affinity=0, should_inline=1, alloc_granularity=4096
real0m24.841s
user2m26.253s
sys0m0.032s
超線程SMT究竟可以快多少?
表格和結(jié)論:
測(cè)試名 | 硬件配置 | 運(yùn)行時(shí)間(s) |
Test6 | “真”6核 | 38.562 |
Test7 | “假”6核 | 58.843 |
Test8 | “真”3核 | 73.175 |
測(cè)試使用的是6個(gè)工作線程。為了減少誤差,增加一點(diǎn)運(yùn)行時(shí)間,每個(gè)線程計(jì)算斐波那契數(shù)列第200億項(xiàng)的值。
對(duì)比Test6和7,可以看到SMT的提升大概在52.6%左右。
測(cè)試記錄:
Test6:別名“真”6核,使用6個(gè)關(guān)閉了SMT的物理核進(jìn)行計(jì)算??偤臅r(shí)為38.562s。
Test7:別名“假”6核,使用3個(gè)開(kāi)啟了SMT的物理核進(jìn)行計(jì)算??偤臅r(shí)為58.843s。
Test8:別名“真”3核,使用3個(gè)關(guān)閉了SMT的物理核進(jìn)行計(jì)算??偤臅r(shí)為1m13.175s。
qhw@qhw-laptop:~/develop/smt_test$ cat test.sh
fibonacci=20000000000
sudo printf ""
./turnoff_smt.sh
time ./smt_test -f $fibonacci -g 4096 -t 6
./turnon_smt.sh
time taskset -c 0-2,6-8 ./smt_test -f $fibonacci -g 4096 -t 6
./turnoff_smt.sh
time taskset -c 0-2,6-8 ./smt_test -f $fibonacci -g 4096 -t 6
./turnon_smt.sh
qhw@qhw-laptop:~/develop/smt_test$ ./test.sh
turn off smt
threads_num=6, fibonacci_max=20000000000, should_set_affinity=0, should_inline=1, alloc_granularity=4096
real0m38.562s
user3m50.786s
sys0m0.000s
turn on smt
threads_num=6, fibonacci_max=20000000000, should_set_affinity=0, should_inline=1, alloc_granularity=4096
real0m58.843s
user5m53.018s
sys0m0.005s
turn off smt
threads_num=6, fibonacci_max=20000000000, should_set_affinity=0, should_inline=1, alloc_granularity=4096
real1m13.175s
user3m39.486s
sys0m0.008s
turn on smt
責(zé)任編輯:haq
-
smt
+關(guān)注
關(guān)注
40文章
2905瀏覽量
69312 -
多線程
+關(guān)注
關(guān)注
0文章
278瀏覽量
19981
原文標(biāo)題:超線程SMT究竟可以快多少?(斐波那契版)
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論