Selain waktu penyimpanan variabel lokal / global, prediksi opcode membuat fungsi lebih cepat.
Seperti jawaban lain menjelaskan, fungsi menggunakan STORE_FAST
opcode di loop. Inilah bytecode untuk loop fungsi:
>> 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
Biasanya ketika sebuah program dijalankan, Python mengeksekusi setiap opcode satu demi satu, melacak stack dan melakukan preforming cek lainnya pada frame stack setelah setiap opcode dieksekusi. Prediksi opcode berarti bahwa dalam kasus-kasus tertentu Python dapat melompat langsung ke opcode berikutnya, sehingga menghindari beberapa overhead ini.
Dalam hal ini, setiap kali Python melihat FOR_ITER
(bagian atas loop), itu akan "memprediksi" itu STORE_FAST
adalah opcode berikutnya yang harus dijalankan. Python kemudian mengintip opcode berikutnya dan, jika prediksi itu benar, ia langsung melompat STORE_FAST
. Ini memiliki efek memeras dua opcode menjadi satu opcode tunggal.
Di sisi lain, STORE_NAME
opcode digunakan dalam loop di tingkat global. Python tidak * tidak * membuat prediksi serupa ketika melihat opcode ini. Sebaliknya, itu harus kembali ke atas evaluasi-loop yang memiliki implikasi yang jelas untuk kecepatan di mana loop dieksekusi.
Untuk memberikan detail teknis lebih lanjut tentang optimasi ini, berikut adalah kutipan dari ceval.c
file ("mesin" mesin virtual Python):
Beberapa opcode cenderung berpasangan sehingga memungkinkan untuk memprediksi kode kedua ketika yang pertama dijalankan. Misalnya,
GET_ITER
sering diikuti oleh FOR_ITER
. Dan FOR_ITER
sering diikuti olehSTORE_FAST
atau UNPACK_SEQUENCE
.
Memverifikasi biaya prediksi uji tunggal berkecepatan tinggi dari variabel register terhadap konstanta. Jika pasangan itu baik, maka predikasi cabang internal prosesor sendiri memiliki kemungkinan keberhasilan yang tinggi, menghasilkan transisi hampir nol-overhead ke opcode berikutnya. Prediksi yang berhasil menghemat perjalanan melalui eval-loop termasuk dua cabangnya yang tidak dapat diprediksi, HAS_ARG
tes dan switch-case. Dikombinasikan dengan prediksi cabang internal prosesor, sukses PREDICT
memiliki efek membuat dua opcode berjalan seolah-olah mereka adalah opcode baru tunggal dengan tubuh digabungkan.
Kita dapat melihat dalam kode sumber untuk FOR_ITER
opcode di mana prediksi STORE_FAST
dibuat:
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
The PREDICT
Fungsi memperluas untuk if (*next_instr == op) goto PRED_##op
yaitu kita hanya melompat ke awal dari opcode diprediksi. Dalam hal ini, kita lompat ke sini:
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;
Variabel lokal sekarang diatur dan opcode berikutnya siap untuk dieksekusi. Python terus melalui iterable hingga mencapai akhir, membuat prediksi sukses setiap saat.
The halaman wiki Python memiliki informasi lebih lanjut tentang bagaimana mesin virtual CPython ini bekerja.