Ini adalah pertanyaan lanjutan untuk jawaban yang saya berikan beberapa hari yang lalu . Sunting: tampaknya OP dari pertanyaan itu sudah menggunakan kode yang saya poskan kepadanya untuk menanyakan pertanyaan yang sama , tetapi saya tidak menyadarinya. Permintaan maaf. Jawaban yang diberikan berbeda!
Secara substansial saya mengamati bahwa:
>>> def without_else(param=False):
... if param:
... return 1
... return 0
>>> def with_else(param=False):
... if param:
... return 1
... else:
... return 0
>>> from timeit import Timer as T
>>> T(lambda : without_else()).repeat()
[0.3011460304260254, 0.2866089344024658, 0.2871549129486084]
>>> T(lambda : with_else()).repeat()
[0.27536892890930176, 0.2693932056427002, 0.27011704444885254]
>>> T(lambda : without_else(True)).repeat()
[0.3383951187133789, 0.32756996154785156, 0.3279120922088623]
>>> T(lambda : with_else(True)).repeat()
[0.3305950164794922, 0.32186388969421387, 0.3209099769592285]
... atau dengan kata lain: memiliki else
klausa lebih cepat terlepas dari if
kondisi yang dipicu atau tidak.
Saya menganggap itu ada hubungannya dengan bytecode berbeda yang dihasilkan oleh keduanya, tetapi adakah yang bisa mengkonfirmasi / menjelaskan secara detail?
EDIT: Sepertinya tidak semua orang dapat mereproduksi timing saya, jadi saya pikir mungkin berguna untuk memberikan beberapa informasi tentang sistem saya. Saya menjalankan Ubuntu 11.10 64 bit dengan python default diinstal. python
menghasilkan informasi versi berikut:
Python 2.7.2+ (default, Oct 4 2011, 20:06:09)
[GCC 4.6.1] on linux2
Berikut adalah hasil pembongkaran di Python 2.7:
>>> dis.dis(without_else)
2 0 LOAD_FAST 0 (param)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 (1)
9 RETURN_VALUE
4 >> 10 LOAD_CONST 2 (0)
13 RETURN_VALUE
>>> dis.dis(with_else)
2 0 LOAD_FAST 0 (param)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 (1)
9 RETURN_VALUE
5 >> 10 LOAD_CONST 2 (0)
13 RETURN_VALUE
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
LOAD_CONST(None); RETURN_VALUE
- tetapi seperti yang disebutkan, itu tidak pernah tercapai) di akhirwith_else
. Saya sangat meragukan kode mati membuat fungsi lebih cepat. Bisakah seseorang memberikandis
pada 2.7?else
danFalse
paling lambat dari semuanya (152ns). Tercepat kedua adalahTrue
tanpaelse
(143ns) dan dua lainnya pada dasarnya sama (137ns dan 138ns). Saya tidak menggunakan parameter default dan mengukurnya dengan%timeit
di iPython.with_else
ini lebih cepat terlihat.Jawaban:
Ini dugaan murni, dan saya belum menemukan cara mudah untuk memeriksa apakah itu benar, tetapi saya punya teori untuk Anda.
Saya mencoba kode Anda dan mendapatkan hasil yang sama,
without_else()
berulang kali sedikit lebih lambat daripadawith_else()
:Mempertimbangkan bahwa bytecode identik, satu-satunya perbedaan adalah nama fungsi. Secara khusus, tes timing melakukan pencarian pada nama global. Coba ganti nama
without_else()
dan perbedaannya menghilang:Dugaan saya adalah bahwa
without_else
memiliki tabrakan hash dengan sesuatu yang lainglobals()
sehingga pencarian nama global sedikit lebih lambat.Sunting : Kamus dengan 7 atau 8 kunci mungkin memiliki 32 slot, jadi atas dasar itu
without_else
memiliki tabrakan hash dengan__builtins__
:Untuk memperjelas cara hashing bekerja:
__builtins__
hashes ke -1196389688 yang mengurangi modulo ukuran tabel (32) berarti disimpan dalam slot # 8 tabel.without_else
hash ke 505688136 yang mengurangi modulo 32 adalah 8 sehingga ada tabrakan. Untuk menyelesaikan perhitungan Python ini:Dimulai dengan:
Ulangi ini sampai kami menemukan slot gratis:
yang memberikannya 17 untuk digunakan sebagai indeks berikutnya. Untungnya itu gratis sehingga loop hanya berulang sekali. Ukuran tabel hash adalah kekuatan 2, begitu
2**i
juga ukuran tabel hash,i
adalah jumlah bit yang digunakan dari nilai hashj
.Setiap probe ke dalam tabel dapat menemukan salah satunya:
Slot kosong, dalam hal ini probing berhenti dan kami tahu nilainya tidak ada dalam tabel.
Slot tidak digunakan tetapi digunakan di masa lalu dalam hal ini kita coba nilai selanjutnya yang dihitung seperti di atas.
Slot penuh tetapi nilai hash penuh yang disimpan dalam tabel tidak sama dengan hash kunci yang kita cari (itulah yang terjadi dalam kasus
__builtins__
vswithout_else
).Slot penuh dan memiliki nilai hash persis yang kita inginkan, maka Python memeriksa untuk melihat apakah kunci dan objek yang kita cari adalah objek yang sama (yang dalam hal ini mereka akan karena string pendek yang bisa menjadi pengidentifikasi diinternir sehingga pengidentifikasi identik menggunakan string yang sama persis).
Akhirnya ketika slot penuh, hash cocok persis, tetapi kunci bukan objek yang identik, maka dan hanya saat itu Python akan mencoba membandingkannya untuk kesetaraan. Ini relatif lambat, tetapi dalam kasus pencarian nama seharusnya tidak benar-benar terjadi.
sumber