在很早的時候,就聽網上的文章說:
python有GIL,所以在單程式內,即使使用多執行緒也無法利用到多核的優勢,同一時刻,python的位元組碼只會執行在一個cpu上。
以前也是奉為真理,直到今天在對自己的python server做效能測試的時候,發現一個python程式的cpu居然達到了120%。
當用c++程式設計的時候,如果使用多執行緒,那麼確實程式cpu超過100%非常正常,但是對python來說,似乎這樣就和網上的文章衝突了。
所以還是決定自己親身試驗一下,編寫程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 |
from thread import start_new_thread def worker(): while 1: #print 1 pass for it in range(0, 15): start_new_thread(worker, ()) raw_input() |
執行環境為: centos6.4 64位, python 2.7.
得到的結果如下:
可以清楚的看到,pid為31199的python程式cpu達到了787.9%,接近理論能達到的最大值 800%。
而上方的8個cpu也分別達到了近100%的利用率。
如果只是按照以上測試結果,確實可以得出的結論:python使用單程式,多執行緒確實能夠使用到多核cpu,並不是網上傳的結論。
但是,還是希望如果有讀者對這塊有更深入的研究能夠進行批評指正,謝謝~
8月15日補充
感謝 la.onger 等幾位博友的討論,現在增加一個測試,用來測試純cpu計算用一個執行緒or多個執行緒完成的總時間的差別,程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import time from threading import Thread LOOPS = 1000000 THREAD_NUM = 10 STEP_SIZE = 94753434 class Test(object): num = 1 def work(self): for it in xrange(0, LOOPS): if self.num > STEP_SIZE: self.num -= STEP_SIZE else: self.num += STEP_SIZE def one_thread_test(self): self.num = 1 begin_time = time.time() for v in xrange(0, THREAD_NUM): self.work() print 'time passed: ', time.time() - begin_time def multi_thread_test(self): self.num = 1 t_list = [] begin_time = time.time() for v in xrange(0, THREAD_NUM): t = Thread(target=self.work) t.start() t_list.append(t) for it in t_list: it.join() print 'time passed: ', time.time() - begin_time t = Test() t.one_thread_test() t.multi_thread_test() |
輸入結果如下:
1 2 |
time passed: 3.44264101982 time passed: 7.22910785675 |
使用多執行緒後,比不用多執行緒還慢
為了與c++版做對比,也開發了c++程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
#include <stdio.h> #include <string.h> #include <stdint.h> #include <iostream> #include <memory> #include <sstream> #include <algorithm> #include <string> #include <vector> #include <set> #include <map> #include <sys/time.h> #include <pthread.h> using namespace std; #define LOOPS 1000000 #define THREAD_NUM 10 #define STEP_SIZE 94753434 class Test { public: Test() {} virtual ~Test() {} void one_thread_test() { this->num = 1; gettimeofday(&m_tpstart,NULL); for (size_t i = 0; i < THREAD_NUM; ++i) { work(); } gettimeofday(&m_tpend,NULL); long long timeuse=1000000*(long long)(m_tpend.tv_sec-m_tpstart.tv_sec)+m_tpend.tv_usec-m_tpstart.tv_usec;//微秒 printf("time passed: %f\n", ((double)timeuse) / 1000000); } void multi_thread_test() { this->num = 1; int ret; vector<pthread_t> vecThreadId;//所有thread的id pthread_attr_t attr; pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); gettimeofday(&m_tpstart,NULL); pthread_t threadId; for (int i = 0; i < THREAD_NUM; i++) { ret= pthread_create(&threadId, &attr, Test::static_run_work, (void*)this); if(ret!=0){ pthread_attr_destroy (&attr); } vecThreadId.push_back(threadId); } pthread_attr_destroy (&attr); for(vector<pthread_t>::iterator it = vecThreadId.begin(); it != vecThreadId.end(); ++it) { pthread_join(*it, NULL); } gettimeofday(&m_tpend,NULL); long long timeuse=1000000*(long long)(m_tpend.tv_sec-m_tpstart.tv_sec)+m_tpend.tv_usec-m_tpstart.tv_usec;//微秒 printf("time passed: %f\n", ((double)timeuse) / 1000000); } void work() { for (size_t i = 0; i < LOOPS; ++i) { if (this->num > STEP_SIZE) { this->num -= STEP_SIZE; } else { this->num += STEP_SIZE; } } } static void* static_run_work(void *args) { Test* t = (Test*) args; t->work(); return NULL; } public: int64_t num; struct timeval m_tpstart,m_tpend; }; int main(int argc, char **argv) { Test test; test.one_thread_test(); test.multi_thread_test(); return 0; } |
輸出結果如下:
1 2 |
time passed: 0.036114 time passed: 0.000513 |
可見,c++版確實效能提高了非常多。
由此可見,python的多執行緒程式設計,在多核cpu利用上確實差一些。