成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

通過研究案例,徹底掌握Python GIL

開發 前端
對于大多數后端Web應用來說,GIL的限制并不是一個約束,因為它們通常受到I/O的限制。在這些應用中,大部分時間只是等待來自用戶、數據庫或下游服務的輸入。

Python因其全局解釋器鎖(GIL)而聲名狼藉。GIL限制了Python解釋器一次只能執行一個線程。在現代多核CPU上,這是一個問題,因為程序無法利用多個核心。不過,盡管存在這種限制,Python仍已成為從后端Web應用到AI/ML和科學計算等領域的頂級語言。

1、訓練數據管道的結構

對于大多數后端Web應用來說,GIL的限制并不是一個約束,因為它們通常受到I/O的限制。在這些應用中,大部分時間只是等待來自用戶、數據庫或下游服務的輸入。系統只需具備并發性,而不一定需要并行性。Python解釋器在執行I/O操作時會釋放GIL,因此當線程等待I/O完成時,就會給另一個線程獲得GIL并執行的機會。

GIL的限制不會影響大多數計算密集型的AI/ML和科學計算工作負載,因為像NumPy、TensorFlow和PyTorch等流行框架的核心實際上是用C++實現的,并且只有Python的API接口。大部分計算可以在不獲取GIL的情況下進行。這些框架使用的底層C/C++內核庫(如OpenBLAS或Intel MKL)可以利用多個核心而不受GIL的限制。

當同時有I/O和計算任務時會發生什么?

2、使用純Python的計算任務

具體來說,可以考慮以下兩個簡單的任務。

import time

def io_task():
    start = time.time()
    while True:
        time.sleep(1)
        wake = time.time()
        print(f"woke after: {wake - start}")
        start = wake
        
def count_py(n):
  compute_start = time.time()
  s = 0
  for i in range(n):
      s += 1
  compute_end = time.time()
  print(f"compute time: {compute_end - compute_start}")
  return s

在這里,通過休眠一秒鐘來模擬一個I/O限制的任務,然后喚醒并打印它休眠了多長時間,然后再次休眠。count_py是一個計算密集型的任務,它簡單地對數字n進行計數。如果同時運行這兩個任務會發生什么?

import threading

io_thread = threading.Thread(target=io_task, daemnotallow=True)
io_thread.start()
count_py(100000000)

輸出結果如下:

woke after: 1.0063529014587402
woke after: 1.009704828262329
woke after: 1.0069530010223389
woke after: 1.0066332817077637
compute time: 4.311860084533691

count_py需要大約4.3秒才能計數到一百萬。但是io_task在同一時間內運行而不受影響,大約在1秒后醒來,與預期相符。盡管計算任務需要4.3秒,但Python解釋器可以預先從運行計算任務的主線程中釋放GIL,并給予io_thread獲得GIL并運行的機會。

3、使用numpy的計算任務

現在,本文將在numpy中實現計數函數,并進行與之前相同的實驗,但這次要計數到一千萬,因為numpy的實現效率更高。

import numpy as np

def count_np(n):
    compute_start = time.time()
    s = np.ones(n).sum()
    compute_end = time.time()
    print(f"compute time: {compute_end - compute_start}")
    return s
  
io_thread = threading.Thread(target=io_task, daemnotallow=True)
io_thread.start()
count_np(1000000000)

輸出結果如下:

woke after: 1.0001161098480225
woke after: 1.0008511543273926
woke after: 1.0004539489746094
woke after: 1.1320469379425049
compute time: 4.1334803104400635

這顯示的結果與上一次實驗類似。在這種情況下,不是Python解釋器預先釋放了GIL,而是numpy自己主動釋放了GIL。

這是否意味著在獨立的線程中同時運行I/O任務和計算任務總是安全的?

4、使用自定義C++擴展的計算任務

現在,本文將用Python的C++擴展實現計數函數。

// importing Python C API Header
#include <Python.h>
#include <vector>

static PyObject *count(PyObject *self, PyObject *args){
  long num;

  if (!PyArg_ParseTuple(args, "l", &num))
         return NULL;
  long result = 0L;
  std::vector<long> v(num, 1L);
  for (long i=0L; i<num; i++) {
    result += v[i];
   }

  return Py_BuildValue("l", result);

}


// defining our functions like below:
// function_name, function, METH_VARARGS flag, function documents
static PyMethodDef functions[] = {
  {"count", count, METH_VARARGS, "Count."},
  {NULL, NULL, 0, NULL}
};

// initializing our module informations and settings in this structure
// for more informations, check head part of this file. there are some important links out there.
static struct PyModuleDef countModule = {
  PyModuleDef_HEAD_INIT, // head informations for Python C API. It is needed to be first member in this struct !!
  "count",  // module name
  NULL,
  -1,
  functions  // our functions list
};

// runs while initializing and calls module creation function.
PyMODINIT_FUNC PyInit_count(void){
  return PyModule_Create(&countModule);
}

可以通過運行python setup.py build來構建擴展,使用以下setup.py

from distutils.core import setup, Extension

count_module = Extension('count', sources=['count.cpp'])

setup(name='python_count_extension',
      versinotallow='0.1',
      descriptinotallow='An Example For Python C Extensions',
      ext_modules=[count_module],
      )

然后,使用作為自定義擴展實現的計數函數運行實驗:

import count 

def count_custom(n):
    compute_start = time.time()
    s = count.count(n)
    compute_end = time.time()
    print(f"compute time: {compute_end - compute_start}")
    return s



io_thread = threading.Thread(target=io_task, daemnotallow=True)
io_thread.start()
count_custom(1000000000)

得到如下結果:

woke after: 4.414866924285889
compute time: 4.414893865585327

在這種情況下,計算任務持有GIL,并阻止I/O線程運行。

Python解釋器只能在兩個Python字節碼指令之間預先釋放GIL,在擴展中,是否自愿釋放GIL取決于擴展的實現。

在這種情況下,本例進行了一個不會影響任何Python對象的瑣碎計算,因此可以在C++的計數函數中使用宏Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS來釋放GIL:

static PyObject *count(PyObject *self, PyObject *args){
  long num;

  if (!PyArg_ParseTuple(args, "l", &num))
         return NULL;
  long result = 0L;
  Py_BEGIN_ALLOW_THREADS
  std::vector<long> v(num, 1L);
  for (long i=0L; i<num; i++) {
    result += v[i];
   }
   Py_END_ALLOW_THREADS

  return Py_BuildValue("l", result);

}

使用這種實現方式,當重新運行實驗時,會得到如下結果:

woke after: 1.0026037693023682
woke after: 1.003467082977295
woke after: 1.0028629302978516
woke after: 1.1772480010986328
compute time: 4.186192035675049

5、結論

在使用Python時,了解GIL是很重要的。在大多數常見情況下,可能不會遇到它的限制。但是,如果使用包裝C/C++庫的第三方Python包(除了標準的NumPy、SciPy、TensorFlow或PyTorch),在涉及到任何重型計算時可能會遇到一些問題。在開發自定義擴展時,最好在進行重型計算之前釋放GIL,以便其他Python線程有機會運行。

責任編輯:武曉燕 來源: Python學研大本營
相關推薦

2024-06-21 09:27:05

2009-09-22 13:10:22

ibmdwSOA

2023-12-15 09:45:21

阻塞接口

2021-01-22 17:57:31

SQL數據庫函數

2021-02-20 23:22:04

Python線程語音

2023-03-09 09:06:13

ChanneGo開發

2024-08-23 08:00:00

2018-07-24 11:05:54

邊緣計算網絡云計算

2009-10-16 10:20:37

Python的GIL

2023-03-10 07:46:55

Go開發Channelselect

2020-10-16 08:26:38

AQS通信協作

2014-06-06 10:47:52

Android視圖大小測量案例

2014-08-05 09:28:39

軟件案例

2022-09-16 08:32:17

Reduxreact

2021-08-11 22:17:48

負載均衡LVS機制

2022-08-26 09:01:07

CSSFlex 布局

2023-10-17 13:50:04

RPAAI

2024-12-27 00:37:46

2022-09-01 08:01:56

Pythongunicorn

2009-11-23 20:32:11

ibmdwSOA
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 午夜看片网站 | 日韩在线视频一区 | 欧洲精品一区 | 久久aⅴ乱码一区二区三区 91综合网 | 亚洲天天 | 天天爽夜夜爽精品视频婷婷 | 午夜视频一区二区三区 | 欧美理论在线观看 | 国产精品一区二区三区在线播放 | 久久久久久国产一区二区三区 | 婷婷成人在线 | 影视先锋av资源噜噜 | 国产区视频在线观看 | 欧美电影免费观看高清 | 国产午夜精品一区二区三区四区 | 人人干在线视频 | 成人免费黄视频 | 久久国产精品免费 | 国产成人精品一区二区三区在线 | 日日天天 | 91久久久久久| 午夜亚洲| 成年视频在线观看 | 亚洲一区二区三区四区av | 日韩一区二区三区四区五区 | 亚洲国产欧美一区二区三区久久 | 欧美视频一级 | 亚洲成人免费视频在线 | 亚洲精品一区在线观看 | 黄色av网站在线免费观看 | 国产精品91视频 | 国产精品久久久久无码av | 二区三区视频 | 欧美日韩在线播放 | 精品久久香蕉国产线看观看亚洲 | 亚洲欧美在线视频 | 精品日韩一区二区 | 中文字幕在线第二页 | 福利片在线看 | 一区二区在线免费播放 | 国产av毛片 |