Cython屏蔽GIL锁实践
環(huán)境:
Ubuntu19.10
四核八線程.
題外話,八線程屬于超線程概念,程序員不可控制,屬于操作系統(tǒng)調(diào)度的工作.
基本概念:
什么時(shí)候使用并發(fā)/并行?
資料中有兩種說(shuō)法:
一種是:
涉及到多核就是并行,單核IO異步就是并發(fā)
另一種是:
整個(gè)系統(tǒng)服務(wù)于IO異步任務(wù)就是并發(fā),涉及到CPU密集就是并行
本文以第一種為準(zhǔn)
--------------------------------
消除GIL鎖為什么會(huì)導(dǎo)致單線程變慢?
參考[6]的回答
Python多線程相當(dāng)于單核多線程[7]
--------------------------------
網(wǎng)上資料的多線程包含:
單核多線程和多核多線程.
單核多線程就IO異步.
多核多線程大多數(shù)情況下指的是多核情況.
線程和并發(fā)有關(guān)系,進(jìn)程和隔離有關(guān)系
根據(jù)[4]
I'd welcome a set of patches into Py3k?only if?the performance for a single-threaded program (and for a multi-threaded but I/O-bound program)?does not decrease”(這個(gè)意思是如果移除GIL鎖,那么單線程性能會(huì)下降,也就是I/O異步性能會(huì)下降)
?
Each Python process gets its own Python interpreter and memory space so the GIL won't be a problem. (每個(gè)進(jìn)程都有一個(gè)GIL鎖)
網(wǎng)上說(shuō)的"Python的多線程是雞肋"的意思指的是多核多線程
有了多線程執(zhí)行異步為啥還要多協(xié)程執(zhí)行IO異步?
根據(jù)[5]:
好處是協(xié)程的開(kāi)銷成本更低.
?
GIL鎖影響的是IO密集還是CPU密集任務(wù)?
The GIL does not have much impact on the performance of I/O-bound multi-threaded programs as the lock is shared between threads while they are waiting for I/O.[4](影響的是CPU密集任務(wù)而不是IO密集任務(wù))
?
GIL鎖在你調(diào)用開(kāi)源庫(kù)的時(shí)候,依然存在嗎?
取決于底層是不是C寫(xiě)的,如果是C寫(xiě)的,屏蔽了GIL鎖,那就不存在,否則就存在
----------------------------------------------------------------------------------------------------------------------
網(wǎng)上關(guān)于屏蔽GIL鎖的資料如下:
[1]屏蔽了GIL鎖,沒(méi)有完整代碼
[3]講了原理,但是沒(méi)有實(shí)操
本篇博客重點(diǎn)解析[2].
GIL鎖每個(gè)進(jìn)程都有一個(gè),鎖的到底是什么?
鎖的是多線程進(jìn)行CPU密集計(jì)算時(shí)候的場(chǎng)景.案例如下[4]:
單線程:
# single_threaded.py import timeCOUNT = 50000000def countdown(n):while n>0:n -= 1start = time.time() countdown(COUNT) end = time.time()print('Time taken in seconds -', end - start)Time taken in seconds - 2.2484254837036133
多線程:
Time taken in seconds - 3.074549674987793
?
多進(jìn)程:
from multiprocessing import Pool import timeCOUNT = 50000000 def countdown(n):while n>0:n -= 1if __name__ == '__main__':pool = Pool(processes=2)start = time.time()r1 = pool.apply_async(countdown, [COUNT//2])r2 = pool.apply_async(countdown, [COUNT//2])pool.close()pool.join()end = time.time()print('Time taken in seconds -', end - start)結(jié)論:
CPU密集任務(wù)速度上,
多核多進(jìn)程>多核多線程>單線程
----------------------------------------------下面是屏蔽GIL鎖實(shí)踐------------------------------------------------------------------------------
回到前面一個(gè)話題,python的多線程真的是雞肋嗎?
并不是,請(qǐng)看下面實(shí)驗(yàn),下面實(shí)驗(yàn)受到[2]的啟發(fā),但是因?yàn)閇2]的代碼不完整,所以我自己想出了一個(gè)demo
我們把count提升100倍,下面是三個(gè)代碼文件:
pycall.c
#include <stdio.h> #include <stdlib.h> // #include <iostream.h> int foo(int a, int b) { long long int count=5000000000;while( count > 0 ){// cout << "a 的值:" << a << endl;count--;if(count==2500000000)printf("計(jì)數(shù)中%lld\n", count);if(count==0)printf("計(jì)數(shù)結(jié)束%lld\n", count);};printf("you input %d and %d\n", a, b); return 1; }因?yàn)槲覀兊认乱M(jìn)行多線程測(cè)試,
所以我們?cè)谏厦娲a中故意加入一些if語(yǔ)句用來(lái)監(jiān)控計(jì)數(shù)過(guò)程,如果多線程生效 那么就會(huì)打印兩次,
如果GIL鎖依然存在,那么整個(gè)運(yùn)行過(guò)程會(huì)比單線程還要慢一倍左右
?
pycall-1thread.py
import ctypes import threading import timeif __name__ == '__main__':start=time.time()my_lib=ctypes.cdll.LoadLibrary("./libpycall.so") t1=threading.Thread(target=my_lib.foo,args=(1,1))t1.start()t1.join()end=time.time()print('耗時(shí)=',end-start)?
pycall-2thread.py
import ctypes import threading import timeif __name__ == '__main__':start=time.time()my_lib=ctypes.cdll.LoadLibrary("./libpycall.so") t1=threading.Thread(target=my_lib.foo,args=(1,1))t2=threading.Thread(target=my_lib.foo,args=(2,2))t1.start()t2.start()t1.join()t2.join()end=time.time()print('耗時(shí)=',end-start)?
運(yùn)行方法:
先生成.so文件
gcc -o libpycall.so -shared -fPIC pycall.c
然后:
| 命令 | 運(yùn)行結(jié)果 |
| python pycall-1thread.py | 計(jì)數(shù)中2500000000 計(jì)數(shù)結(jié)束0 you input 1 and 1 耗時(shí)= 10.828569173812866 |
| python pycall-2thread.py | 計(jì)數(shù)中2500000000 計(jì)數(shù)中2500000000 計(jì)數(shù)結(jié)束0 you input 1 and 1 計(jì)數(shù)結(jié)束0 you input 2 and 2 耗時(shí)= 10.749252796173096 |
可以看到,雙線程運(yùn)行與單線程運(yùn)行速度一致,真正發(fā)揮了多核的威力.
所以明白如何使用python的多線程嗎?
在關(guān)鍵部位使用C/C++代碼來(lái)撰寫(xiě),屏蔽GIL鎖,大大提升計(jì)算速度.
C/C++寫(xiě)起來(lái)如果覺(jué)得繁瑣,可以參考o(jì)pencv,很多可以直接拿來(lái)用喔
?
?
Reference:
[1]Python解決GIL鎖的辦法
[2]python的GIL鎖優(yōu)化方案及性能對(duì)比
[3]GIL鎖
[4]What is the Python Global Interpreter Lock (GIL)?
[5]線程和進(jìn)程的區(qū)別是什么? - 無(wú)與童比的回答 - 知乎
[6]python中去掉gil會(huì)大幅降低單線程的執(zhí)行速度,怎么理解?
[7]為什么有人說(shuō) Python 的多線程是雞肋呢? - DarrenChan陳馳的回答 - 知乎
[8]Python 的GIL與線程鎖
[9]Python 中的 GIL 如果可以去掉的話,是不是只要有線程鎖就完全不會(huì)產(chǎn)生負(fù)面影響?
[10]對(duì)于多線程程序,單核cpu與多核cpu是怎么工作的
[11]單核多線程與多核多線程的區(qū)別---總結(jié)
[12]【什么時(shí)候用多線程——CPU篇】
[13]協(xié)程池
[14]多線程有什么用?
[15]How does a single CPU handle Multi-threaded and multi-process applications?
總結(jié)
以上是生活随笔為你收集整理的Cython屏蔽GIL锁实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 网络大数据有风险还能在哪里网贷(网贷平台
- 下一篇: semaphore的使用