Python, yalnızca dönüş değeri olarak kullanılan bir değişkeni optimize ediyor mu?


106

Aşağıdaki iki kod parçacığı arasında nihai bir fark var mı? İlki, bir işlevdeki bir değişkene bir değer atar ve ardından bu değişkeni döndürür. İkinci işlev, değeri doğrudan döndürür.

Python bunları eşdeğer bayt koduna dönüştürür mü? Biri daha hızlı mı?

Durum 1 :

def func():
    a = 42
    return a

Durum 2 :

def func():
    return 42

5
Her dis.dis(..)ikisinde de kullanırsanız , bir fark olduğunu görürsünüz , yani evet. Ancak çoğu gerçek dünya uygulamasında, işlevdeki işlemenin gecikmesine kıyasla bunun ek yükü o kadar değildir.
Willem Van Onsem

4
İki olasılık vardır: (a) Bu işlevi sıkı bir döngüde birçok (yani en az bir milyon) kez çağıracaksınız. Bu durumda, bir Python işlevi çağırmamalısınız, bunun yerine döngünüzü numpy kitaplığı gibi bir şey kullanarak vektörleştirmelisiniz. (b) Bu işlevi pek çok kez çağırmayacaksınız. Bu durumda, bu işlevler arasındaki hız farkı endişelenmeye değmeyecek kadar azdır.
Arthur Tacca

Yanıtlar:


138

Hayır değil .

CPython bayt kodunun derlenmesi, yalnızca temel optimizasyonları gerçekleştirmek için tasarlanmış küçük bir gözetleme deliği optimize ediciden geçirilir ( Bu optimizasyonlar hakkında daha fazla bilgi için test paketindeki test_peepholer.py'ye bakın ).

Gerçekte ne olacağına bakmak için, disoluşturulan talimatları görmek için * kullanın. Atamayı içeren ilk işlev için:

from dis import dis
dis(func)
  2           0 LOAD_CONST               1 (42)
              2 STORE_FAST               0 (a)

  3           4 LOAD_FAST                0 (a)
              6 RETURN_VALUE

İkinci işlev için ise:

dis(func2)
  2           0 LOAD_CONST               1 (42)
              2 RETURN_VALUE

İlkinde iki (hızlı) talimat daha kullanılır: STORE_FASTve LOAD_FAST. Bunlar, hızlı bir şekilde depolanır ve fastlocalsgeçerli yürütme çerçevesinin dizisindeki değeri alır . Daha sonra her iki durumda da a RETURN_VALUEgerçekleştirilir. Yani, ikinci zamankinden böyledir biraz daha hızlı çalıştırmak için gereken daha az komutları nedeniyle.

Genel olarak, CPython derleyicisinin gerçekleştirdiği optimizasyonlarda tutucu olduğunu unutmayın. Diğer derleyiciler kadar akıllı değildir ve olmaya çalışmaz (genel olarak üzerinde çalışacak çok daha fazla bilgi vardır). Açıkça doğru olmanın yanı sıra ana tasarım hedefi, a) basit tutmak ve b) bunları derlerken olabildiğince hızlı olmaktır, böylece bir derleme aşamasının var olduğunu bile fark etmezsiniz.

Sonunda, bunun gibi küçük sorunlarla uğraşmamalısın. Hızdaki fayda minik, sabittir ve Python'un yorumlanması gerçeğinin getirdiği ek yükten çok daha azdır.

* diskodunuzu parçalara ayıran küçük bir Python modülüdür, onu sanal makinenin çalıştıracağı Python bayt kodunu görmek için kullanabilirsiniz.

Not: @Jorn Vernee tarafından yapılan bir yorumda da belirtildiği gibi, bu Python'un CPython uygulamasına özgüdür. CPython isterlerse diğer uygulamalar daha agresif optimizasyonlar yapabilir.


11
Bir python insanı değil (c ++), bu yüzden kaputun altında nasıl çalıştığını bilmiyorum ama ilk durum ikinci duruma göre optimize edilmemeli mi? İyi bir C ++ derleyicisi bu optimizasyonu yapacaktır.
NathanOliver

7
@NathanOliver gerçekten öyle değil, Python akıllıca oynamaya bile kalkışmadan burada anlatıldığı gibi yapacak.
Dimitris Fasarakis Hilliard

80
@ NathanOliver'in bu soruya verilen cevabındaki mükemmel mantıklı ve zekice tahmininin tamamen yanlış olması, bana göre bunun cevaplanabilecek "kendi kendini açıklayıcı", "saçma", "aptal" bir soru olmadığının kanıtıdır. TigerhawkT3'ün inanmamızı isteyeceği gibi, "düşünmek için bir dakikanızı ayırarak". Yıllardır profesyonel bir Python programcısı olmama rağmen cevabından emin olmadığım geçerli ve ilginç bir soru.
Mark Amery

Python'un derleyicisi en iyi ihtimalle "muhafazakar", "çok muhafazakar" değil. Ana tasarım hedefi "olabildiğince hızlı olmak ... böylece bir derleme aşamasının var olduğunu bile farketmezsiniz." Bu, "basit tut" dan sonra ikincildir. "1 << (2 ** 34)" ve "b'x '* (2 ** 32)" gibi büyük sabitlere sahip bir işlevin derlenmesi birkaç saniye sürer ve işlev hiçbir zaman olmasa bile GB boyutunda sabitler oluşturur Çalıştırmak. Büyük dizge derleyici tarafından bile atılacaktır. Bu durumlar için önerilen düzeltmeler, derleyiciyi çok karmaşık hale getireceğinden reddedildi.
Andrew Dalke

@AndrewDalke içeriden bu konuda yorum yaptığınız için teşekkürler, belirttiğiniz sorunları ele almak için ifadelerde ince ayar yaptım.
Dimitris Fasarakis Hilliard

3

Her ikisi de temelde aynıdır, ancak ilk durumda nesnenin 42adı verilen bir değişkene atanması aveya başka bir deyişle, adlar (yani a) değerleri (yani 42) ifade eder. Hiçbir veriyi kopyalamaması anlamında teknik olarak herhangi bir görevlendirme yapmaz.

İşlem sırasında return, bu adlandırılmış bağlama ailk durumda döndürülürken, nesne 42ikinci durumda döndürülür.

Daha fazla okuma için Ned Batchelder'ın bu harika makalesine bakın

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.