cPythonインタプリタ

8519 ワード

cPythonインタプリタ


導入


Pythonは、過去10年間で最も人気のあるプログラミング言語の一つです.
構文は読みやすいですが、Pythonとは考えにくい部分があります.
多くの場合、Pythonはマルチスレッドを行うときに遅いと思います.これは主にPythonのPythonの実装によるものです.
この記事では、GILとは何か、どのように動作しているのか、なぜPythonにとって重要なのかを説明します.

メモリ管理


Pythonのメモリ管理がPythonのPythonの公式実装でどのように行われるかを理解しましょう.Pythonのすべてのオブジェクトには、その値、その型、および参照カウンタが格納されます.

リファレンスカウンタは、オブジェクトがメモリから削除されなければならないかどうかを決定するために使用します.あるオブジェクトへのリファレンスを追加するとき、リファレンスカウンタがインクリメントされ、参照を削除したときにデクリメントを行う.参照カウンタがゼロのとき、メモリから削除されます.つまり、オブジェクトを参照することができないということです.
参照カウントフィールドはsysを使用して調べることができます.GetRefCount機能(この関数によって返される値は、関数が呼び出されたときに、オブジェクトへの参照を持つときには常に1になります)
>>> import sys
>>> A="toto"
>>> B=A
>>> sys.getrefcount(A)
3
aの参照カウントはsys . 2の前の2行に加えて3です.GetRefCountも参照してください.それは直接我々はこの関数を実行した後に2にドロップします.
変数cでaを参照して、何が起こるかを見ましょう.
>>> C=A
>>> sys.getrefcount(A)
4
現在、リファレンスは1増加し、4になります.
del ()関数を使用すると、オブジェクトをメモリから削除して参照を削除できます.
>>> del(C)
>>> sys.getrefcount(A)
3
ビンゴ!参照を削除するので、リファレンスは1によって減少します.
それはかなり良い.しかし、プログラムの複数の部分が同時に参照カウンタで変更する場合.マルチスレッドの概念でこれが起こることができます.thread 1とthread 2の両方が変数を使用していることを想像してください.thread 1は、Thread 2がそれを増加させるのと同時にリファレンスカウンタの値を減少させます.これは決して他のスレッドがまだ参照を持っている間に決してリッチなゼロのリファレンスカウンタまたはゼロに到達することができます.これはPythonがGILを使う理由の一つです.それで、本当にGILです.
どれもギル?
GILかグローバルインタプリタロックは、PythonとNODEJSのような言語で使用されているミューテックスやロックで、一度に1つのスレッドしか実行できません.これは、安全なメモリ管理を確保し、単一スレッドプログラムをより速くする.1992年8月にGuido van Rossumにより導入された.
それで、GILはCPythonのために何をしますか
バックマルチスレッドとリファレンスカウンタGILの問題と大きな解決策をもたらす.つのスレッドだけがいつでも実行されますので、1つのスレッドが実行中に参照カウンタの変更を行うことができます.これはGILがPythonで使われる主な理由の一つです.しかし、cpythonが多くのC拡張とライブラリを使用するという事実もあります.これらの拡張モジュールはメモリと直接対話することができ、メモリの問題を引き起こす可能性があります.GILは、ロックシステムでこれを防ぐことができますので、安全な方法でこれらの拡張モジュールを実行できます.また、シングルスレッドプログラムのパフォーマンス向上を提供します.
それは肯定的な側面です.しかし、GILの他の面について.
上記のように、1つのスレッドだけをいつでも実行することができます.それを実際に見てみましょう.
私たちは、同じことをする2つのプログラムを比較するつもりです:与えられた数から戻ること.
最初のスレッドはシングルスレッドです:
import time 


def count_back(start):
    while start > 0:
        start = start - 1

START = 50000000

t1 = time.time()
count_back(START)

print("total execution time is: ", time.time() - t1)

total execution time is: 3.8295717239379883


我々が見ることができるように、それはカウントをするために3、83秒かかります.
では、マルチスレッドプログラムを使ってください.
import time
from threading import Thread

def count_back(start):
    while start > 0:
        start = start - 1

START = 50000000

# creation of threads
thread1 = Thread(target=count_back, args=(START//2,))
thread2 = Thread(target=count_back, args=(START//2,))

# execution
t1 = time.time()
thread1.start()
thread2.start()
thread1.join()
thread2.join()

print("total execution time is: ", time.time() - t1)

total execution time is: 4.143504619598389


私は2コアCPUでそれを実行します.
それはシングルスレッドよりも速くなるはずでしたが、そうではありません.さらに時間がかかる.
Pythonはマルチコアプロセッサのマルチスレッドプログラムでもシングルスレッドのように実行されます.
これはPythonのように、特にマルチスレッドプログラムでは遅いです.マルチスレッドでどのようにGILが動作するかを理解しましょう.

どのようにギル仕事?


スレッド起動時にGILを最初に取得する必要があります.また、いくつかのIO操作を終了したり、何らかの操作をしなければならない場合には、GILを解放し、次のスレッドがGILを取得して実行することができます.
それは400 mリレーを走らせるようです.あらゆるランナーが糸であると仮定しましょう、そして、棒はGILです.誰かが実行するには、彼は前にスティックを取得する必要があります.それがGILの仕事です.それは単純な愚かですが、それは特にシングルスレッドプログラムで素晴らしい作品.パフォーマンスの向上は本当に重要なことができます.Python 2からPython 3に、Antoine PitroによってGILの内部でなされたいくつかの顕著な改善がありました.そして、それはpython 3 gilをより信頼できるようにします.
あなたは、以下のこのイメージでそれを視覚化することができます.
同時実行する2つのスレッドの例

代替案GIL


GILはPython CPythonの独自の実装でプログラムを遅くすることができます.
いくつかの実装では、Gylを使用しません.あなたのPythonプログラムをより速く走らせたいなら、Guidoは言います.
PYPYで上記のプログラムの出力です.

total execution time is: 0.13152098655700684


また、PythonではPythonでパフォーマンスを処理するためのPythonでのマルチ処理もあります.それぞれのプロセスごとに1つのインタプリタを実行します.

結論


私たちは、ギルがPythonの重要な部分であるのを見ました.たとえあなたのプログラムが遅くなるとしても、それはPythonのためにより利点を提供します.これは、Pythonのように、今日のCの拡張子の簡単なサポートを人気にする理由の一つです.
プログラマとしては本当にGILの影響を見ることはできませんが、それは常に良いホイールの背後にある知っている.それがこの記事です.
あなたが楽しむ望み.