ローカル/グローバル変数のストア時間以外に、オペコードの予測は、機能を高速化します。
他の答えが説明するように、関数はループ内でSTORE_FAST
オペコードを使用します。ここで関数のループのためのバイトコードは、次のとおり
>> 13 FOR_ITER 6 (to 22) # get next value from iterator
16 STORE_FAST 0 (x) # set local variable
19 JUMP_ABSOLUTE 13 # back to FOR_ITER
通常
プログラムが実行されたとき、Pythonはスタックを追跡し、各オペコードが実行された後のスタックフレーム上の他のチェックを予備、各オペコード次々に実行します。オペコード予測は、あるケースでは、Pythonが次のオペコードに直接ジャンプできるため、このオーバーヘッドの一部を回避することを意味します。
この場合、PythonはFOR_ITER
(ループの先頭)を見るたびに、実行する次のオペコードであるSTORE_FAST
を「予測」します。 Pythonは次のオペコードを覗いて予測が正しければ、それはまったくSTORE_FAST
にジャンプします。これにより、2つのオペコードを1つのオペコードに絞り込む効果があります。
一方、オペコードがSTORE_NAME
でグローバルレベルのループで使用されています。 Pythonはではなく、はこのオペコードを見ると同様の予測をします。その代わりに、ループが実行される速度に明白な意味を持つ評価ループの先頭に戻る必要があります。いくつかのオペコードは、このようにすることが可能となってペアで来る傾向にある
:この最適化に関するいくつかの技術的な詳細を与えることを
は、ここceval.c
ファイル(Pythonの仮想マシンの「エンジン」)からの引用です は、最初のコードが実行されたときに2番目のコードを予測します。たとえば、 GET_ITER
の後には、多くの場合、FOR_ITER
が続きます。 FOR_ITER
は、多くの場合、 であり、その後にはSTORE_FAST
またはUNPACK_SEQUENCE
となります。
予測の検証では、レジスタの高速高速テストである が定数に対して変わります。ペアリングが良好だった場合、 プロセッサ自身の内部分岐予測は、 の成功確率が高く、次のオペコード にほぼゼロオーバヘッドで遷移します。予測が成功すると、eval-loop に2つの予期しない分岐、HAS_ARG
テストと スイッチケースが含まれています。プロセッサの内部分岐予測と組み合わせると、 成功したPREDICT
は、2つのオペコードを のように実行する効果があります。これらは、ボディを結合した単一の新しいオペコードです。私たちは予測しオペコードの先頭にジャンプすなわちPREDICT
機能がif (*next_instr == op) goto PRED_##op
に展開
case FOR_ITER: // the FOR_ITER opcode case
v = TOP();
x = (*v->ob_type->tp_iternext)(v); // x is the next value from iterator
if (x != NULL) {
PUSH(x); // put x on top of the stack
PREDICT(STORE_FAST); // predict STORE_FAST will follow - success!
PREDICT(UNPACK_SEQUENCE); // this and everything below is skipped
continue;
}
// error-checking and more code for when the iterator ends normally
:私たちはSTORE_FAST
の予測が行われた正確FOR_ITER
オペコードのソースコードに見ることができます
。この場合、ここでジャンプします:
PREDICTED_WITH_ARG(STORE_FAST);
case STORE_FAST:
v = POP(); // pop x back off the stack
SETLOCAL(oparg, v); // set it as the new local variable
goto fast_next_opcode;
ローカル変数が設定され、次のオペコードが実行されます。 Pythonは、終わりに達するまでイテレートを継続し、毎回成功した予測を行います。
Python wiki pageには、CPythonの仮想マシンがどのように機能するかについての詳細があります。
実際にタイミングをどのようにしましたか? –
Python 3.2.3 REPLの動作が確認されました。面白い。 – Deestan
ちょうど直感、それが本当であるかどうかわからない:私はそれがスコープのためだと思うだろう。関数の場合、新しいスコープが作成されます(つまり、値にバインドされた変数名を持つハッシュの種類)。関数がなければ、変数はグローバルスコープにあります。多くのものが見つかると、ループが遅くなります。 – Scharron