※記事内に商品プロモーションを含むことがあります。
はじめに
Pythonの関数において、毎回決まった値(定数)を必要としたい場合があります。たとえば、物理シミュレーションを実行したいとき、パラメータなどが定数になります。
この定数をどのように記述すれば処理が速くなるか検証しました。
結論から書くと、以下の4つの方法では、上の方法ほど実行が速くなります。
- 定数を変数に代入せず、処理に直接埋め込む。
- 関数の外に、定数をグローバル変数として定義する。
- 関数の中に定数をローカル変数として定義する。
- 定数を関数の引数にする。
ただし、どの方法を使っても実行速度は1割程度しか変化しないため、ソースコードの可読性やメンテナンス性を重視したほうが良いと思います。
検証した環境は以下の通りです。
- OS: Windows 10 Home
- CPU: Intel Celeron G3930
- Memory: 8GB
- Python 3.8.8
関数と定数
この記事では、以下のように関数の中で定数を使用したいケースを考えます。
|
|
CONST
が定数です。例のように簡単な関数の場合はCONST
という定数を定義せずに直接y = 3.14*x
とした方が良いですが、定数を繰り返し使う場合や、後々値を変更する可能性がある場合は定義した方が良いです。
しかし、この例では関数を呼び出す度にCONST = 3.14
の処理が実行されるのではと思ったので、どのように定数を記述すれば高速に処理できるのか測定しました。
なお、Pythonには厳密な定数(上書き不可能な変数)を記述する仕組みがありませんが、コード実行時に変更されない変数のことを便宜的に「定数」と呼んでいます。
実行したコード
処理時間を測定するコードを以下に示します。
|
|
定数の記述方法が異なる以下の4つの関数を作りました。
multi_global
: 定数をグローバル変数として記述multi_local
: 定数をローカル変数として記述multi_embedded
: 定数を定義せず、処理に直接記述multi_arg
: 定数を引数にとる
multi_local
では、定数に代入する処理が毎回入るので遅そうですが、可読性が高いため作りました。
multi_embedded
では可読性やメンテナンス性が落ちますが、処理が一番速そうです。
また、実行時間の測定にはtimeit.timeit
関数を用いています。引数のnumber
は繰り返し回数です。
コードを実行すると、各関数の平均実行時間を表示します(処理に1分程度掛かります)。
実行結果
実行結果は以下の通りです。
|
|
平均実行時間をナノ秒で示しています。
予想通り、一番高速なのは定数を埋め込んだmulti_embedded
です。
次に速いのが、グローバル変数を参照するmulti_global
です。
上位2つから時間が空いて、ローカル変数を毎回定義するmulti_local
が3位となりました。定数の代入にはキャッシュが効いていないようです。
最も遅かったのが、定数を引数にとるmulti_arg
でした。
ただし、最も速いmulti_embedded
と最も遅いmulti_arg
を比較しても、差は13%程度しかありません。
よほど実行速度が問題になる状況でもない限り、可読性やメンテナンス性を考慮した方が良いと思います。
まとめ
関数内で定数を使用する場合、以下の4つの方法では、上の方法ほど実行が速くなります。
- 定数を変数に代入せず、処理に直接埋め込む。
- 関数の外に、定数をグローバル変数として定義する。
- 関数の中に定数をローカル変数として定義する。
- 定数を関数の引数にする。
ただし、どの方法を使っても実行速度は1割程度しか変化しないため、ソースコードの可読性やメンテナンス性を重視したほうが良いです。