4年間で5倍高速化を目指すFaster CPython 「Python3.11」の正式リリース
こんにちは!PA Labメディアです。
今回の記事では待ち望まれていたFaster Pythonのプロジェクトによって進められていた高速化プロジェクトの一部が実装されたPython 3.11を紹介していきます。正式リリースは2022年10月3日に行われる予定です。
今回の記事では以下のような方を対象にしております。
- 「Pythonをよく使っているけど、3.11のリリース内容での変更を知りたい」
- 「具体的にPythonがどのぐらい早くなるのかを知らない」
- 「Faster CPythonのプロジェクトは知っていたけど、具体的な速度向上方法はどうやっているのだろう」
本記事は上記のような方を対象にした記事となっています。
Python歴が10年以上の筆者が分かりやすく今回のアップデートに関して紹介を行っていきます。
目次
Pythonとは
Pythonはできることが幅広く、今では世界中で使われている人気のプログラミング言語です。
こちらの記事では初心者に向けてPythonで出来ることに関しての説明を詳しく行っているので、未読の方は是非読んでみてください。
Pythonでよく使用される統合開発環境に関してはこちらの記事を参照してください。
Python3.11のインストール方法
この記事は2022年の9月11日時点で執筆されており、正式版リリースの2022年10月3日より前の記事となっているため、執筆時点ではマニュアルでインストールしています。
正式リリースは2022年10月3日となっているため、それ以降では正式バージョンをインストールしましょう。
Pyenvをインストール済みの方
$ pyenv install 3.11
Python3.11に導入される新機能一覧
最も大きい変更としては後方互換性を保ちながら、Python3.10よりも10-60%の高速化を行っている点になります。平均でも1.25倍の高速化が行われており、単純にPython3.11に切り替えるだけで1.25倍高速化が可能になります。
まずは、メインである高速化以外の仕様として追加される機能を紹介していきます。
Python3.11に導入される新機能一覧(1) 文法の追加
- PEP 654: Exception Groups and
except*
新しく提案されたExceptionGroup
と except*
と いう例外の型が追加されました。
これにより無関係の複数の例外処理をグループ化して処理する事が出来るようになりました。
Trioと呼ばれるサードパーティのライブラリのマルチエラーハンドリングにインスパイアを受けて開発をされた機能ですが、例えば、並行処理時のエラー処理時に複数の例外をハンドリングする事が可能になります。
>>> eg = ExceptionGroup(
... "one",
... [
... TypeError(1),
... ExceptionGroup(
... "two",
... [TypeError(2), ValueError(3)]
... ),
... ExceptionGroup(
... "three",
... [OSError(4)]
... )
... ]
... )
>>> import traceback
>>> traceback.print_exception(eg)
| ExceptionGroup: one (3 sub-exceptions)
+-+---------------- 1 ----------------
| TypeError: 1
+---------------- 2 ----------------
| ExceptionGroup: two (2 sub-exceptions)
+-+---------------- 1 ----------------
| TypeError: 2
+---------------- 2 ----------------
| ValueError: 3
+------------------------------------
+---------------- 3 ----------------
| ExceptionGroup: three (1 sub-exception)
+-+---------------- 1 ----------------
| OSError: 4
+------------------------------------
>>> type_errors = eg.subgroup(lambda e: isinstance(e, TypeError))
>>> traceback.print_exception(type_errors)
| ExceptionGroup: one (2 sub-exceptions)
+-+---------------- 1 ----------------
| TypeError: 1
+---------------- 2 ----------------
| ExceptionGroup: two (1 sub-exception)
+-+---------------- 1 ----------------
| TypeError: 2
+------------------------------------
>>>
ExceptionGroup
という定義が新しく導入されており、ExceptionGroup
そのものを入れ子にする事もできるような形で例外処理が可能になります。またこの処理はexcept*
を用いて、簡単に書く事も出来ます。
従来のtry-excpet文では処理すべき例外は一つなので、except句の本体は例外にマッチした最初の一つのみが実行される仕組みになっています。今回導入された構文では、1つの例外グループが複数のexcept節を実行する事が可能になります。
具体的な例としては以下を見ていただくと早いと思います。ExceptionGroup
を発生させて、複数のexcept節を作成してみましょう。
try:
raise ExceptionGroup('msg', [TypeError(1), ExceptionGroup('msg2', [ValueError(2), IOError(3)])])
except* TypeError:
print("TypeError")
pass
except* ValueError:
print("ValueError")
pass
except* (IOError, TypeError):
print("MixedError")
pass
結果として、従来のexcept節とは違って複数のexcept節を経由して実行する事が可能になります。
TypeError
ValueError
MixedError
Python3.11に導入される新機能一覧(2) 組み込み機能の追加
PEP 678: Enriching Exceptions with Notes.
Exception全般にadd_note
というメソッドが追加されて、例外発生時に新しい情報を追記出来るようになりました。例えば、pytestでのテスト時に発生するテストに関しての詳細を追記する事が可能になりました。
try:
raise TypeError('bad type')
except Exception as e:
e.add_note('Add some information')
raise
結果
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: bad type
Add some information
Python3.11に導入される新機能一覧(3) 標準ライブラリの追加
PEP 680: tomllib
— Support for Parsing TOML in the Standard Library.
Python3ではTOMLという拡張子のファイルを扱う際にサードパーティ製のライブラリをインストールする必要がありましたが、標準ライブラリに追加されることになりました。
TOMLは以下のような設定ファイルです。個人的にはTOMLはconfigファイルを書く際には便利な仕様だと思っているので、今回のアップデートは嬉しいですね。
# TOMLドキュメントの例です。
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00 # 日付はファーストクラスのデータ型です。
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
[servers]
# タブもしくはスペースで自由にインデントできます。
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ]
# 配列内の改行ももちろんOK!
hosts = [
"alpha",
"omega"
]
Python3.11に導入される新機能一覧(4) インタプリタの向上
PEP 657: Include Fine Grained Error Locations in Tracebacks.
この仕様はトレースバック時に詳細なエラーの場所を提示してくれる、というインタプリタ側での機能追加になります。
具体的なコードとしては
def foo(x):
1 + 1/0 + 2
def bar(x):
try:
1 + foo(x) + foo(x)
except Exception as e:
raise ValueError("oh no!") from e
bar(bar(bar(2)))
実際にこれを実行すると以下のような結果になります。
今回のアップデートにより、今までのPythonでは表示されていなかった具体的なエラーの場所に関して詳細に表示してくれるようになります。デバッグ時に非常に強力なサポートになりますね。
Traceback (most recent call last):
File "test.py", line 6, in bar
1 + foo(x) + foo(x)
^^^^^^
File "test.py", line 2, in foo
1 + 1/0 + 2
~^~
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "test.py", line 10, in <module>
bar(bar(bar(2)))
^^^^^^
File "test.py", line 8, in bar
raise ValueError("oh no!") from e
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: oh no
Python3.11に導入される新機能一覧(4) 豊富な型サポート機能の追加
PEP 646: Variadic generics.
PEP 655: Marking individual TypedDict items as required or potentially missing.
PEP 673: Self
type.
PEP 675: Arbitrary literal string type.
PEP 681: Data Class Transforms.
今回は量が多いので割愛しますが、上記の型サポートに関する新しい仕様も沢山追加されています。
それでは次にPython3.11に導入される最も大きな変更であるFaster CPython
プロジェクトに関しての詳細を見ていきましょう。
Python3.11に導入されるFaster CPython
Python3.11に導入されるFaster CPython(1) 概要とベンチマーク
概要
全体としてPython 3.11ではPython3.10よりも10-60%の高速化となっていて、平均でも1.25倍の高速化が行われています。もちろん後方互換性はあるので、Python3.11に切り替えるだけで高速化が見込めます。
ベンチマーク
Faster CPythonの最初のリリースとなるPython 3.11での実際の高速化について比較をしてみました。
Python公式が出しているベンチマークpyperformanceの結果について可視化をしてみました。
一部だけ遅くなっている関数もありますが、全体的にはとても速度が改善されていることが分かりますね。
Python3.11に導入されるFaster CPython(2) Faster CPythonの歴史的な経緯
前述したようにPythonでは便利で人気の高い言語ですが、速度面での懸念がありました。
インタプリタそのもをJITコンパイラベースにするなど様々な方向性での高速化が今までにも考えられていましたが、なかなか現実的な形で実現する事がありませんでした。
ただ、Mark Shannon氏がPythonのフォーラムにて、Faster CPythonプロジェクトを提案した事がきっかけで始まったことです。mark自身の高速化のアイデアは妥当なものが多く、実際にMicorosftに在籍するGuido van Rossum(グイド・ヴァンロッサム)氏も加わって出資を受けながら開発を進められています。
出資ありきでの提案書を書いて実装を進めて実際に出資を得る、というスタンスはとても大胆ですが、OSSやSoftwareの業界にとってはとても健全なあり方ですね。
Mark氏の提案では毎年50%の高速化を行い、4年間で5倍の高速化を目標としています。
ちなみに動画でのインタビューにおいて、グイド・ヴァンロッサム氏は今後のアップデートでマシン語に生成される処理が実現される事に対して言及をしているので、Python3.12、もしくはそれ以降のアップデートでは数倍の高速化が現実的に見込まれているようです。
Python3.11に導入されるFaster CPython(3) 関数呼び出しのインライン化
高速化の文脈ではこれまでのPython3系は大きく異なる部分の1つとして、関数呼び出しのインライン化と呼ばれる処理があります。PythonJPの記事でも大きく取り上げられている改善点になります。
Pythonのインタプリタの仕組みとして、Pythonのソースコードをバイトコードに変換した後に、順次実行をしていく仕組みとなっています。今までのPythonでは「swtich」命令を用いた実装になっていましたが、スレッド化されたコードの中では「goto」を使用する事で高速化する事が可能になっています。
具体的に言うと、再帰関数のような処理ではcevalループと呼ばれるPythonのコアで使用されている箇所を何度も呼び出す必要がありました。今回のアップデートではその代わりに関数の実行情報のみを更新する方法に変更が行われました。
ただし、こちらはcevalループを利用するようなPythonの処理に限定して早くなるので、あくまでも一部の箇所だけ高速化する形となります。具体的にはPythonの組み込み関数 map
では内部でC言語が使用されているため、Python3.10と3.11では大きく変化することはありません。
Python3.11に導入されたFaster CPython(4) 特殊化適応的インタープリタ
こちらもPythonJPの記事でも大きく取り上げられている高速化のテクニックの一つになります。
この特殊化適合的という処理自体はJITコンパイラにてよく見られる最適化ですが、インタプリタにおいても導入するメリットが大きく、最大で50%以上の高速化が見込めます。
例えば、2つの引数a, bの足し算の結果を計算する関数を考えてみましょう。
def add_num(num_of_apple, num_of_banana):
return num_of_apple, num_of_banana
Pythonはプログラムを実行する際に、ソースコードをコンパイルしてバイトコードへの変換を行います。この時生成されるコードはどの型でも対応できるような一般(Generalized)のバイトコードです。
生成されるバイトコードではどの型でも対応できるようになっていますが、どの型に対応できるようになっているので処理としては遅くなりますが、int
型が入るとわかっている場合には最適化する事でより早いバイトコード(特殊化されたバイトコード)が生成する事が出来ます。
しかし実際のケースでは何回も呼び出される関数の型は一意に決まっているケースが殆どです。
例えば今回の add_num
ではりんごの個数とバナナの個数を足し算する処理になっていて、どちらの引数もintが想定されているように見えます。
今回の実装では何回も実行されるような処理(適応的)に対して最適化をしたバイトコードを生成するようにします。初回の生成時には処理が少しかかりますが、何回も同じ型の引数が入った関数が呼ばれる場合、全体としての処理速度はとても早くなります。
まとめ
今回はPython3.11のアップデートに関して紹介をしてきました。個人的にはここ最近のメジャーアップデートで一番大きな変更が入っている、と思いました。
Pythonは初心者にはわかりやすく書きやすい言語の上にライブラリも豊富ですが、速度面でもデメリットが問題視されていました。Faster CPythonのプロジェクトにより今後のPythonは5倍速度向上を掲げているので、今後のPython需要はさらに高まっていく事が考えられます。
そして今回の記事でも重要な事ですが、Python3.11にバージョンがアップデート出来る状態であれば、とりあえずアップデートするだけでも理論的には1.25倍の高速化が見込めます。
PA Labでは「AIを用いた自動化×サービス開発」の専門家として活動をしています。高度なデータ分析からシステム開発まで一貫したサービス提供を行っており、特に機械学習やディープラーニングを中心としたビジネス促進を得意としております。
無料で分析設計/データ活用に関するご相談も実施中なので、ご相談があればお問い合わせまで。
この記事へのコメントはありません。