timeit
パタシュが示唆するように、推測しようとするのではなく、テストに使用しましょう。で魔法%timeit
を使いますipython
。もっと単純だからです。コードは次のとおりです。
In [275]: def ff(): pass
In [276]: def ft(): pass
In [277]: def f1(b): # naive implementation
.....: for i in range(1000000):
.....: if b: ft()
.....: else: ff()
In [278]: %timeit f1(True)
10 loops, best of 3: 117 ms per loop
In [279]: def f2(b): # DSM's implementation
.....: f = ft if b else ff
.....: for i in range(1000000):
.....: f()
In [280]: %timeit f2(True)
10 loops, best of 3: 99.2 ms per loop
したがって、少なくとも私の Mac の CPython 3.3.0 64 ビットでは、少し高速です。
ただし、Python の最適化について少しでも知っていれば、グローバル変数をローカル変数に移動した場合とほぼ同じパフォーマンスの向上であることに気付くでしょう。したがって、ブール式を巻き上げずに同じことを行うことで、方程式からそれを取り除きましょう。
In [277]: def f3(b): # Just local binding, no if hoisting
.....: f, g = ft, ff
.....: for i in range(1000000):
.....: if b: f()
.....: else: g()
In [286]: %timeit f3(True)
10 loops, best of 3: 94.8 ms per loop
OPの意図した最適化と、変更なしで3.xおよび2.xで動作するコードを含む、より完全なテストをまとめ、 Apple 2.7.2、python.org 3.3.0、PyPy 1.9.0/に対して実行しました2.7.2、および Jython 2.5.2 (Mac ですべて 64 ビット ビルド、次に Cython 0.17.1 pyximport (Python 3.3.0 の下) を使用して Cython コードと同じソースをコンパイルします。
3.3.0 2.7.2 PyPy Jython Cython
orig 1.136 1.519 0.091 1.680 0.448
OP optimization 1.119 1.362 0.034 1.613 0.460
rebinding 0.936 1.369 0.030 1.492 0.137
DSM version 0.936 1.329 0.031 1.523 0.138
したがって、ループの外側で名前をバインドすると、速度が 1.1 倍から 3 倍になるようです。さらに、比較をループの外に引き上げると、さらに 3% ほどの結果が得られる可能性がありますが、これはすべて、CPython の代わりに PyPy を使用すること、Python の代わりに Cython を使用すること、または 2.x の代わりに 3.x を使用することと比較して何もありません。実際の Cython またはカスタム C コードを作成するか、ループを に移動するnumpy
と、さらに高速になります。
そして考えてみれば、これは理にかなっている。10 億回の bool 比較またはグローバル ルックアップのコストが重要である場合、10 億回の関数呼び出しと 10 億回のインタープリター ループのコストは、はるかに重要になります。それを最適化することに煩わされていない場合(そして、map
インタープリターを切り替えたり、コードを書き換えたりしても、ループの代わりにジェネレーター式、リスト内包表記、呼び出しなどを使用するだけでそれを行うことができますnumpy
。実現不可能です)、小さなことを気にする必要はありません。
そして明らかに、最後の 3% が実際に違いを生む場合は、実際に関心のあるプラットフォームで、より現実的なテストを実行する必要があります。
おそらく DSM の実装を使用する価値はありますが、より慣用的で読みやすいためであり、高速である場合とそうでない場合があるからではありません。