Yerel / global değişken mağaza sürelerinin yanı sıra, opcode tahmini işlevi daha hızlı hale getirir.
Diğer cevapların açıkladığı gibi, fonksiyon STORE_FAST
döngüdeki opcode'u kullanır . İşte işlevin döngüsü için bayt kodu:
>> 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
Normalde bir program çalıştırıldığında, Python her opcode'u birbiri ardına yürütür, bir yığının kaydını tutar ve her opcode yürütüldükten sonra yığın karesinde diğer kontrolleri önceden yapar. Opcode tahmini, bazı durumlarda Python'un bir sonraki opcode'a doğrudan atlayabileceği ve böylece bu ek yükün bir kısmından kaçınabileceği anlamına gelir.
Bu durumda, Python her FOR_ITER
döngüde (döngünün üst kısmı) her gördüğünde STORE_FAST
, yürütmesi gereken bir sonraki opcode olan "tahmini" olur. Python daha sonra bir sonraki opcode'a göz atar ve tahmin doğruysa doğrudan doğruya atlar STORE_FAST
. Bunun, iki opodu tek bir opcode olarak sıkma etkisi vardır.
Öte yandan, STORE_NAME
opcode döngüde global düzeyde kullanılır. Python, bu opcode'u gördüğünde benzer tahminlerde bulunmaz * . Bunun yerine, döngünün yürütüldüğü hız için belirgin etkileri olan değerlendirme döngüsünün en üstüne geri dönmelidir.
Bu optimizasyon hakkında daha fazla teknik ayrıntı vermek için, ceval.c
dosyadan bir alıntı (Python'un sanal makinesinin "motoru"):
Bazı opodlar çiftler halinde gelir, böylece ilk kod çalıştırıldığında ikinci kodu tahmin etmeyi mümkün kılar. Örneğin
GET_ITER
, genellikle onu takip eder FOR_ITER
. Ve FOR_ITER
genellikleSTORE_FAST
veya ile devam eder UNPACK_SEQUENCE
.
Tahmini doğrulamak, bir kayıt değişkeninin bir sabite karşı tek bir yüksek hızlı testine mal olur. Eşleştirme iyiyse, işlemcinin kendi dahili şube tahmininin yüksek bir başarı olasılığı vardır ve bu da bir sonraki opcode'a neredeyse sıfır ek yüke geçişle sonuçlanır. Başarılı bir tahmin, öngörülemeyen iki dalı olan HAS_ARG
test ve anahtar durumu da dahil olmak üzere eval-loop üzerinden bir yolculuk kaydeder . İşlemcinin dahili şube tahmini ile birleştiğinde, başarılı bir şekilde PREDICT
, iki opcodun, gövdeleri birleştirilmiş yeni bir tek opcodemuş gibi çalıştırması etkisi vardır.
FOR_ITER
Opcode'un kaynak kodunda tam olarak tahminin STORE_FAST
yapıldığı yeri görebiliriz:
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
PREDICT
Fonksiyon için genişler if (*next_instr == op) goto PRED_##op
biz sadece tahmin işlem kodu başlangıcına atlamak yani. Bu durumda, buraya atlıyoruz:
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;
Yerel değişken ayarlanmıştır ve bir sonraki opcode yürütülmeye hazırdır. Python her seferinde başarılı bir tahminde bulunarak sonuna kadar tekrarlanabilir.
Python wiki sayfası CPython sanal makine nasıl çalıştığı hakkında daha fazla bilgi vardır.