亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享

Python 的 GIL

2023-04-23 09:24:01
7
0

引言
聊一個老生長談的問題: Python的GIL。
結合日常工作的總結和前人的經驗,聊一聊我自己對此GIL的理解。
希望本文對Python的初學者有一些幫助。

到底什么是GIL?

GIL全稱: Global Interpreter Lock。注意這里有個Interpreter(Cython、Jython),說白了這把鎖其實是加在解釋器上的,這把鎖只允許一個Python線程獲得解釋器控制權,簡單說就是某一時刻只能有一個線程運行(單線程)。對GIL褒貶不一,對于單線程程序來講其實沒有什么影響,但是對于多線程程序,有的時候就會成為影響性能的瓶頸點。對GIL的形容,你可能在網上還會找到類似臭名昭著的形容詞,其實我覺著萬事都不能這么絕對,它存在既有存在的原因和用處。

GIL對Python到底有什么幫助?

我們知道Python使用了計數的方式來管理對象的回收,進而管理內存。簡單來說就是每個對象都會有個計數,當這個計數變為0的時候,回收機制就會回收這個對象,釋放對象占用的內存空間。看一個簡單的例子:

1
2
3
4
5
>>> import sys
>>> a = 4902384823
>>> b = a
>>> sys.getrefcount(a)
3

 

通過getrefcount可以拿到某個對象的計數,插個小插曲,為什么我這里用這么大的數(4902384823), 為什么不用1?細心的讀者可以去網上找一下,Python的小整數池,你就會明白。

我們繼續聊GIL,想一下這個場景,如果解釋器允許多線程并發執行,都要對這個a做操作,會有什么樣的結果產生呢?線程1要把a賦值給c,線程2呢,要把a賦值成別的值(原來的a對象技術就會減小1),線程3也要把a賦值成別的值,線程4更狠,直接釋放了a,線程5….,試想一下,當出現這么多的進程要操作同一個對象的時候,情況就復雜了,是不是會導致程序的邏輯異常甚至直接崩潰呢?GIL就派上了用場,在某個時刻只允許一個線程運行,很完美的解決了上述問題。不排除還有其他的垃圾回收機制,比如golang就十分復雜,不得不引入STW機制,來進行垃圾回收,要知道在Python誕生之際,操做系統還沒有線程的概念。

有人可能會問,那不能給對象加把鎖么?這樣不就可以同時運行多個線程了么?那就會需要數量龐大的鎖,維護這些鎖開銷很大,另外很可能會觸發死鎖(多鎖情況下的陷阱)情況。而GIL只需要管理一個鎖,能提高單個線程的性能。

由于Python簡單易用,目前越來越多的人開始學習和使用Python,這其中GIL起著很大的作用。C庫的許多擴展,有的需要在Python中實現其功能,而GIL則提供了線程安全的內存管理,可以防止不一致的更改,GIL對Python的快速發展起著不可估量的作用。

另外GIL只需要管理著一個鎖,將變得很簡單,這在早期的Python設計開發中,GIL是個結合實際情況而做出合適的選擇。

有這個GIL我該怎么提升我的代碼性能呢?

要想提高代碼性能,首先得先弄清楚你得代碼是面向什么場景的。只有結合了實際,才能選出適合的方案來。咱們拋開業務講,無非就是兩種情況,CPU 計算瓶頸約束和 I/O 瓶頸約束。

CPU 密集計算型

你的程序是極其耗CPU的,最簡單的,就是下邊這個程序,他可能會跑滿你某個核心

1
2
3
a = 0
while 1:
a += 1

 

你的CPU會不停的工作,甚至累趴下,這會程序已經沒有優化的余地了,別說Python,就是任何語言也得望而止步,就好比是你開的車,你已經把擋掛到最高,把油門踩到底了,你還怎么加速,只有這個速度了。比較典型的還有圖片計算、視頻計算等等也都是耗CPU的計算。

I/O 型

什么是 I/O 型呢?網絡訪問、磁盤讀寫這都是典型的 I/O 操作,比如你訪問google,你的I/O開銷可就大多了,我想會有人應看到TIMEOUT,即便你訪問baidu,嗖的一下打開的網頁,那段時間,對于計算機世界來說那也是極為漫長的等待,而這段時間你的CPU幾乎提前進入了退休生活,一個字,閑。

磁盤訪問也是一種 I/O 操作,磁盤如果是老式機械磁盤,那也是慢的出奇,即便后來的SSD相對于CPU時間片段來講也漫長的很。

提升思路

  • 計算密集型(以下基于單核)
    對于CPU密集型來講,無能為力,如果本身一個CPU 1秒只能計算一個結果,你就是換成什么語言,什么架構它也不可能超過一個,最好的情況是等于1個,但基本達不到,為什么?

    先說Python,線程維護是有開銷的,這個CPU得做,所以怎么可能到1個呢?有的初學者可能考慮換個語言加多線程,那可能比單線程還慘,多線程來回上下文切換,更耗資源

    不說硬件的提升,光說代碼,這時候應該考慮算法本身的提升,比如圖片計算里,考慮換成矩陣計算會不會好些?或者看是不是有些計算是多余等等。當然如果考慮硬件的提升,那就沒有止境了。

  • I/O 型(以下基于單核)
    對于Python, I/O密集型的應用還是有提升的余地的。如果說I/O等待占據了大部分時間,我們這會可以考慮“多線程”(如果已1秒為單位,即便是GIL存在,那也會有n個線程被執行),如果I/O等待的時間遠遠大于獲取GIL(全局大鎖)的時間,那多線程勢必會提升速度。

    多進程,雖然只有一個CPU,還是要看進程切換的時間和I/O等待的時間的對比,如果I/O等待時間很長,那多進程之間的切換開銷也就微不足道了。

    協程,這個是個好東西,在一個線程里邊,協程切換是用戶態的,開銷小的多,這也是提升性能的利劍。

  • 其他
    多核情況下,不管計算密集型還是I/O 型,多進程一定是能提升性能,畢竟一個人干活跟10個人干活肯定速度是不一樣的。

GIL依然存在

那能不能去掉GIL呢?答案顯然是可以的,不然golang怎么做的?java怎么做的?可是為什么不去掉GIL呢,我認為還是歷史原因,首先很多C庫是基于GIL做的,推翻了重整,你懂得,很難。另外如果真的完全去掉GIL,那將是從里到外的巨大改進,前后兼容又成了巨大的問題。但是改進肯定有的。

0條評論
作者已關閉評論
yunson
9文章數
0粉絲數
yunson
9 文章 | 0 粉絲

Python 的 GIL

2023-04-23 09:24:01
7
0

引言
聊一個老生長談的問題: Python的GIL。
結合日常工作的總結和前人的經驗,聊一聊我自己對此GIL的理解。
希望本文對Python的初學者有一些幫助。

到底什么是GIL?

GIL全稱: Global Interpreter Lock。注意這里有個Interpreter(Cython、Jython),說白了這把鎖其實是加在解釋器上的,這把鎖只允許一個Python線程獲得解釋器控制權,簡單說就是某一時刻只能有一個線程運行(單線程)。對GIL褒貶不一,對于單線程程序來講其實沒有什么影響,但是對于多線程程序,有的時候就會成為影響性能的瓶頸點。對GIL的形容,你可能在網上還會找到類似臭名昭著的形容詞,其實我覺著萬事都不能這么絕對,它存在既有存在的原因和用處。

GIL對Python到底有什么幫助?

我們知道Python使用了計數的方式來管理對象的回收,進而管理內存。簡單來說就是每個對象都會有個計數,當這個計數變為0的時候,回收機制就會回收這個對象,釋放對象占用的內存空間。看一個簡單的例子:

1
2
3
4
5
>>> import sys
>>> a = 4902384823
>>> b = a
>>> sys.getrefcount(a)
3

 

通過getrefcount可以拿到某個對象的計數,插個小插曲,為什么我這里用這么大的數(4902384823), 為什么不用1?細心的讀者可以去網上找一下,Python的小整數池,你就會明白。

我們繼續聊GIL,想一下這個場景,如果解釋器允許多線程并發執行,都要對這個a做操作,會有什么樣的結果產生呢?線程1要把a賦值給c,線程2呢,要把a賦值成別的值(原來的a對象技術就會減小1),線程3也要把a賦值成別的值,線程4更狠,直接釋放了a,線程5….,試想一下,當出現這么多的進程要操作同一個對象的時候,情況就復雜了,是不是會導致程序的邏輯異常甚至直接崩潰呢?GIL就派上了用場,在某個時刻只允許一個線程運行,很完美的解決了上述問題。不排除還有其他的垃圾回收機制,比如golang就十分復雜,不得不引入STW機制,來進行垃圾回收,要知道在Python誕生之際,操做系統還沒有線程的概念。

有人可能會問,那不能給對象加把鎖么?這樣不就可以同時運行多個線程了么?那就會需要數量龐大的鎖,維護這些鎖開銷很大,另外很可能會觸發死鎖(多鎖情況下的陷阱)情況。而GIL只需要管理一個鎖,能提高單個線程的性能。

由于Python簡單易用,目前越來越多的人開始學習和使用Python,這其中GIL起著很大的作用。C庫的許多擴展,有的需要在Python中實現其功能,而GIL則提供了線程安全的內存管理,可以防止不一致的更改,GIL對Python的快速發展起著不可估量的作用。

另外GIL只需要管理著一個鎖,將變得很簡單,這在早期的Python設計開發中,GIL是個結合實際情況而做出合適的選擇。

有這個GIL我該怎么提升我的代碼性能呢?

要想提高代碼性能,首先得先弄清楚你得代碼是面向什么場景的。只有結合了實際,才能選出適合的方案來。咱們拋開業務講,無非就是兩種情況,CPU 計算瓶頸約束和 I/O 瓶頸約束。

CPU 密集計算型

你的程序是極其耗CPU的,最簡單的,就是下邊這個程序,他可能會跑滿你某個核心

1
2
3
a = 0
while 1:
a += 1

 

你的CPU會不停的工作,甚至累趴下,這會程序已經沒有優化的余地了,別說Python,就是任何語言也得望而止步,就好比是你開的車,你已經把擋掛到最高,把油門踩到底了,你還怎么加速,只有這個速度了。比較典型的還有圖片計算、視頻計算等等也都是耗CPU的計算。

I/O 型

什么是 I/O 型呢?網絡訪問、磁盤讀寫這都是典型的 I/O 操作,比如你訪問google,你的I/O開銷可就大多了,我想會有人應看到TIMEOUT,即便你訪問baidu,嗖的一下打開的網頁,那段時間,對于計算機世界來說那也是極為漫長的等待,而這段時間你的CPU幾乎提前進入了退休生活,一個字,閑。

磁盤訪問也是一種 I/O 操作,磁盤如果是老式機械磁盤,那也是慢的出奇,即便后來的SSD相對于CPU時間片段來講也漫長的很。

提升思路

  • 計算密集型(以下基于單核)
    對于CPU密集型來講,無能為力,如果本身一個CPU 1秒只能計算一個結果,你就是換成什么語言,什么架構它也不可能超過一個,最好的情況是等于1個,但基本達不到,為什么?

    先說Python,線程維護是有開銷的,這個CPU得做,所以怎么可能到1個呢?有的初學者可能考慮換個語言加多線程,那可能比單線程還慘,多線程來回上下文切換,更耗資源

    不說硬件的提升,光說代碼,這時候應該考慮算法本身的提升,比如圖片計算里,考慮換成矩陣計算會不會好些?或者看是不是有些計算是多余等等。當然如果考慮硬件的提升,那就沒有止境了。

  • I/O 型(以下基于單核)
    對于Python, I/O密集型的應用還是有提升的余地的。如果說I/O等待占據了大部分時間,我們這會可以考慮“多線程”(如果已1秒為單位,即便是GIL存在,那也會有n個線程被執行),如果I/O等待的時間遠遠大于獲取GIL(全局大鎖)的時間,那多線程勢必會提升速度。

    多進程,雖然只有一個CPU,還是要看進程切換的時間和I/O等待的時間的對比,如果I/O等待時間很長,那多進程之間的切換開銷也就微不足道了。

    協程,這個是個好東西,在一個線程里邊,協程切換是用戶態的,開銷小的多,這也是提升性能的利劍。

  • 其他
    多核情況下,不管計算密集型還是I/O 型,多進程一定是能提升性能,畢竟一個人干活跟10個人干活肯定速度是不一樣的。

GIL依然存在

那能不能去掉GIL呢?答案顯然是可以的,不然golang怎么做的?java怎么做的?可是為什么不去掉GIL呢,我認為還是歷史原因,首先很多C庫是基于GIL做的,推翻了重整,你懂得,很難。另外如果真的完全去掉GIL,那將是從里到外的巨大改進,前后兼容又成了巨大的問題。但是改進肯定有的。

文章來自個人專欄
文章 | 訂閱
0條評論
作者已關閉評論
作者已關閉評論
0
0