《Python實戰進階》 No46:CPython的GIL與多線程優化

Python實戰進階 No46:CPython的GIL與多線程優化


摘要

全局解釋器鎖(GIL)是CPython的核心機制,它保證了線程安全卻限制了多核性能。本節通過concurrent.futuresC擴展優化多進程架構,實戰演示如何突破GIL限制,特別針對AI模型推理加速場景,提供可直接復用的性能優化方案。


在這里插入圖片描述

核心概念與知識點

1. GIL的本質與限制Python

  • 工作原理:每個線程執行前必須獲取GIL,CPython通過周期性切換(默認5ms)實現偽并行
  • 致命缺陷:CPU密集型任務無法利用多核(如神經網絡推理)
  • 例外場景:C擴展釋放GIL期間可并行執行(如NumPy矩陣運算)

2. 突破GIL的三大武器

方法原理適用場景典型性能提升
多進程(multiprocessing)進程隔離繞過GILCPU密集型任務核心數倍
C擴展并發在C層面釋放GIL已封裝的底層計算(如OpenCV)2-10x
異步IO(asyncio)單線程事件循環I/O密集型任務1.5-5x

3. GIL感知型編程原則

# 判斷當前是否持有GIL(需Python 3.12+)
import sys
sys._is_gil_enabled()  # 返回布爾值

實戰案例:AI模型推理加速

場景模擬

使用ResNet50模型進行圖像分類,對比不同架構的吞吐量表現

案例1:純多線程陷阱(threaded_infer.py)
from concurrent.futures import ThreadPoolExecutor
import numpy as np
import timedef inference(image):# 模擬模型推理(實際調用TensorFlow/PyTorch)np.dot(image, np.random.rand(3072, 1000))  # 觸發NumPy底層C運算return "class_id"def benchmark(n_threads=8):image = np.random.rand(1, 3072)start = time.time()with ThreadPoolExecutor(max_workers=n_threads) as executor:results = list(executor.map(inference, [image]*100))print(f"Threads: {n_threads}, Time: {time.time()-start:.2f}s")if __name__ == "__main__":benchmark()

運行結果

Threads: 8, Time: 3.25s   # CPU核心數8
Threads: 1, Time: 3.18s   # 單線程反而更快?

結論:多線程在CPU密集型任務中因GIL競爭反而更慢!


案例2:多進程突圍(process_infer.py)

from concurrent.futures import ProcessPoolExecutorif __name__ == "__main__":benchmark(n_threads=8)  # 替換為ProcessPoolExecutor

性能對比

架構并行度耗時CPU利用率
單線程13.18s12%
多線程83.25s15%
多進程80.89s98%

案例3:C擴展魔法(numpy_gil_release.py)

import numpy as np
import threadingdef numpy_kernel():a = np.random.rand(5000, 5000)b = np.random.rand(5000, 5000)start = time.time()np.dot(a, b)  # NumPy在BLAS中釋放GILprint(f"Dot product done in {time.time()-start:.2f}s")# 啟動多個線程同時計算
threads = [threading.Thread(target=numpy_kernel) for _ in range(4)]
for t in threads: t.start()

實測結果

4個線程同時執行,總耗時僅比單次計算多15%
CPU利用率飆升至380%(4核8線程CPU)

AI大模型相關性分析

1. PyTorch DataLoader的多進程黑科技

from torch.utils.data import DataLoader, Datasetclass MyDataset(Dataset):def __len__(self): return 1000def __getitem__(self, i): # 這里會自動在子進程中執行return np.random.rand(3,224,224)loader = DataLoader(MyDataset(), batch_size=32, num_workers=4)
  • 性能提升:4個worker使數據預處理速度提升3.2倍
  • GIL規避原理:每個worker是獨立進程,不受主進程GIL限制

2. ONNX Runtime的線程控制

import onnxruntime as ort# 設置線程數(繞過GIL限制的CPU并行)
ort_sess = ort.InferenceSession("model.onnx")
ort_sess.set_providers(['CPUExecutionProvider'], [{'intra_op_num_threads': 8}])

總結與擴展思考

技術決策樹(CPU密集型任務)

是否需要多核?
├─ 否 → 使用線程池(I/O任務)
└─ 是 → 需突破GIL├─ 可用C擴展? → NumPy/OpenCV向量化└─ 否則 → 多進程架構(注意IPC開銷)

Jupyter安全多線程實踐

# 避免在Notebook主線程中啟動過多線程
import nest_asyncio
nest_asyncio.apply()  # 解除asyncio嵌套限制# 推薦模式:將多進程邏輯封裝在子函數中
def run_pool():with ProcessPoolExecutor() as e:return e.submit(my_task).result()
%time run_pool()  # 在cell中安全調用

Cython無GIL擴展(add.pyx)

# distutils: language_level=3
from libc.math cimport sqrt
import numpy as np
cimport numpy as npdef vector_norm(np.ndarray[np.float64_t, ndim=1] arr):cdef double res = 0.0cdef int i, N = arr.shape[0]with nogil:  # 關鍵:釋放GILfor i in range(N):res += arr[i] * arr[i]res = sqrt(res)return res

編譯后可被多個線程同時調用,完全繞過GIL限制


💡 思考題:為什么NumPy的np.dot在多線程下能實現近乎線性的加速,但純Python矩陣乘法卻不行?

下期預告:No47 內存優化大師課:從對象序列化到共享內存的極致壓縮技巧

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/79062.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/79062.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/79062.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Golang實現函數默認參數

golang原生不支持默認參數 在日常開發中,我們有時候需要使用默認設置,但有時候需要提供自定義設置 結構體/類,在Java我們可以使用無參、有參構造函數來實現,在PHP中我們也可以實現(如 public function xxx($isCName false, $sec…

Chrome 136 H265 WebRTC 支持 正式版本已包含

時間過的真快,去年8月份寫過一篇文章介紹如何加參數方式啟動Chrome H265 硬件解碼器, 現在的136版本已經包含在內,至此WebRTC已經完整包含了H264和H265解碼器,這個事情應該從2015年開始,Google強推VP9 AV1&#xff0c…

12.SpringDoc OpenAPI 功能介紹(用于生成API接口文檔)

12.SpringDoc OpenAPI 功能介紹(用于生成API接口文檔) SpringDoc OpenAPI 是一個基于 OpenAPI 3.0/3.1 規范的工具,用于為 Spring Boot 應用生成 API 文檔。它是 springfox(Swagger 2.x)的現代替代方案,完全支持 Spring Boot 3.x…

CentOS Linux 環境二進制方式安裝 MySQL 5.7.32

文章目錄 安裝依賴包新建用戶解壓初始化配置文件啟動服務登錄MySQL修改密碼停止數據庫 安裝依賴包 yum -y install libaio perl perl-devel libncurses* autoconf numactl新建用戶 useradd mysql解壓 tar xf mysql-5.7.32-linux-glibc2.12-x86_64.tar.gz mv mysql-5.7.32-l…

Webug4.0通關筆記06- 第8關CSV注入

目錄 CSV注入漏洞 1.CSV漏洞簡介 2.漏洞原理 (1)公式執行 (2)DDE機制 (3)OS命令執行 3.漏洞防御 第08關 CSV注入 1.打開靶場 2.修改源碼 3.注入命令 4.導出excel表 5.打開excel表 CSV注入漏洞…

Windows和 macOS 上安裝 `nvm` 和 Node.js 16.16.0 的詳細教程。

Windows和 macOS 上安裝 nvm 和 Node.js 16.16.0 的詳細教程。 --- ### 1. 安裝 nvm(Node Version Manager) nvm 是一個 Node.js 版本管理工具,可以輕松安裝和切換不同版本的 Node.js。 #### Windows 安裝 nvm 1. **下載 nvm 安裝包**&#x…

[特殊字符] 藍橋杯省賽全解析:含金量、獲獎難度、參賽意義與發展價值全面剖析

藍橋杯省賽剛剛落幕,不論你是剛參加完比賽的同學,還是還在觀望是否值得投入時間去準備藍橋杯的學生,相信你都關心: 藍橋杯到底值不值得參加? 獲獎難不難?含金量如何? 和其它算法競賽相比有什么…

ASP.NET MVC后端控制器用模型 接收前端ajax數據為空

1、前端js代碼 如下: const formData {DeptName: D001,Phone: 12345678900 };$.ajax({url: "/Phone/SavePhone1",type: "POST",contentType: "application/json",data: JSON.stringify(formData), //必須要JSON.stringifysuccess:…

擁抱 Kotlin Flow

1. 引言 Kotlin Flow 是 Kotlin 協程生態中處理異步數據流的核心工具,它提供了一種聲明式、輕量級且與協程深度集成的響應式編程模型。與傳統的 RxJava 相比,Flow 更簡潔、更易于維護,尤其在 Android 開發中已成為主流選擇。本文將從基礎概念…

精益數據分析(34/126):深挖電商運營關鍵要點與指標

精益數據分析(34/126):深挖電商運營關鍵要點與指標 在創業和數據分析的學習之旅中,我們都在不斷探尋如何讓業務更上一層樓。今天,我依舊帶著和大家共同進步的想法,深入解讀《精益數據分析》中電商運營的關…

Learning vtkjs之ImageCropFilter

過濾器 圖片數據體積裁剪 介紹 vtkImageCropFilter可以裁剪vtkImageData。這只適用于IJK對齊的平面。 請注意,由于CPU限制的裁剪,這在大型數據集上會很慢。 效果 核心代碼 需要實現這個代碼主要邏輯 1、設定的crop的包圍盒 其實主要是IMax IMin JM…

深入理解 C++11 delete 關鍵字:禁用函數的藝術

一、什么是 delete 關鍵字 C11 引入的 delete 關鍵字是一種??顯式禁用函數??的語法機制。它允許開發者主動阻止特定函數的使用,比傳統的私有化聲明更直觀、更安全,且能在編譯期捕獲更多潛在錯誤。 二、為什么需要 delete? 1. 傳統方式…

深度剖析!GPT-image-1 API 開放對 AI 繪畫技術生態的沖擊!

4月24日凌晨,OpenAI正式發布了全新的圖像生成模型“gpt-image-1”,并通過API向全球開發者開放使用,這意味著其GPT-4o的圖像生成能力正式向開發者開放! 在這之前,GPT-4o的圖像生成功能于今年3月25日由 OpenAI 創始人兼 …

扣子流程圖批量導入飛書多維表格

文章目錄 整體結構分步驟進行處理1. 程序代碼處理2. 多維表格配置 整體結構 整個代碼塊結構如下: 首先,我們從其他流程中拿到一個數據列表,通過一個循環體,將每一個部分的內容都通過python代碼整理后,使用【插件】的…

【安全掃描器原理】端口掃描

【安全掃描器原理】端口掃描 1.端口掃描基本原理2.TCP掃描3.UDP掃描4.手工掃描1.端口掃描基本原理 以TCP端口為例,其原理是當一個主機向遠端一個服務器的某一個端口提出建立連接的請求,如果對方有此項服務,就會同意建立連接,如果對方未安裝此項服務時,則不會同意建立連接…

FastGPT部署的一些問題整理

在B站學習 圖靈程序員-諸葛 的LangChain快速入門課程之《部署FastGPT構建本地應用》。在我學習課程跟著老師實踐的過程中,踩了一些坑。這篇文章以問答的形式記錄一下學習中的一些問題,主要面向的讀者是,在學習同樣的課程的和部署FastGPT遇到各…

如何查看k8s獲取系統是否清理過docker鏡像

k8s集群某個節點down掉后,pod就會漂移到其他節點,但是在該節點卻又執行了拉取鏡像操作,明明該節點之前部署過該容器的,不知為什么又拉取了一次鏡像(鏡像拉取配置的優先使用本地),所以懷疑是觸發…

聚焦智能體未來,領馭科技在微軟創想未來峰會大放異彩

2025年4月23日,微軟創想未來峰會在北京中關村國際創新中心盛大舉行。作為微軟中國南區核心合作伙伴及HKCSP 1T首批授權云服務商,深圳領馭科技有限公司受邀參會,攜瀚鵬工業AI應用解決方案亮相峰會,與全球AI領袖及行業精英共話智能體…

元宇宙2.0:當區塊鏈成為數字世界的憲法

引言:當虛擬世界成為“新大陸” 清晨,你戴上VR設備進入一個由數字建筑構成的城市,這里的地皮屬于全球玩家,街邊的藝術品標著NFT認證碼,咖啡館里的人們用加密貨幣支付咖啡,而社區規則由持有代幣的居民投票決…

力扣hot100——239.滑動窗口最大值

題目鏈接: 239. 滑動窗口最大值 - 力扣(LeetCode) 優先級隊列 優先級隊列自動按照大小排序,隊首即為最大元素,但取隊首時要注意元素是否在滑動窗口內,如果不在則彈出。 class Solution { public:vector&…