Saya berharap array.array
lebih cepat dari daftar, karena array tampaknya tidak dikotak.
Namun, saya mendapatkan hasil berikut:
In [1]: import array
In [2]: L = list(range(100000000))
In [3]: A = array.array('l', range(100000000))
In [4]: %timeit sum(L)
1 loop, best of 3: 667 ms per loop
In [5]: %timeit sum(A)
1 loop, best of 3: 1.41 s per loop
In [6]: %timeit sum(L)
1 loop, best of 3: 627 ms per loop
In [7]: %timeit sum(A)
1 loop, best of 3: 1.39 s per loop
Apa yang bisa menjadi penyebab perbedaan seperti itu?
python
arrays
performance
boxing
python-internals
Valentin Lorentz
sumber
sumber
array
paket. Jika Anda ingin melakukan sejumlah besar matematika, Numpy beroperasi dengan kecepatan cahaya (yaitu C), dan biasanya lebih baik daripada implementasi yang naif sepertisum()
).array
cukup cepat dalam mengubah string bilangan bulat (mewakili byte ASCII) kestr
objek. Guido sendiri hanya menemukan ini setelah banyak solusi lain dan cukup terkejut dengan kinerjanya. Bagaimanapun ini adalah satu-satunya tempat di mana saya ingat melihatnya bermanfaat.numpy
jauh lebih baik untuk berurusan dengan array tetapi ketergantungan pihak ke-3.Jawaban:
The penyimpanan adalah "unboxed", tapi setiap kali Anda mengakses sebuah elemen Python harus "kotak" itu (menanamkan dalam sebuah objek Python biasa) untuk melakukan apa-apa dengan itu. Misalnya, Anda
sum(A)
mengulangi array, dan kotak setiap integer, satu per satu, dalamint
objek Python biasa . Itu menghabiskan waktu. Di Andasum(L)
, semua tinju dilakukan pada saat daftar itu dibuat.Jadi, pada akhirnya, sebuah array pada umumnya lebih lambat, tetapi membutuhkan memori yang jauh lebih sedikit.
Berikut kode yang relevan dari versi terbaru Python 3, tetapi ide dasar yang sama berlaku untuk semua implementasi CPython sejak Python pertama kali dirilis.
Berikut kode untuk mengakses item daftar:
Ada sangat sedikit untuk itu:
somelist[i]
hanya mengembalikani
objek th dalam daftar (dan semua objek Python di CPython adalah pointer ke sebuah struct yang segmen awal sesuai dengan tata letak astruct PyObject
).Dan inilah
__getitem__
implementasi untukarray
kode tipel
:Memori mentah diperlakukan sebagai vektor
C
long
bilangan bulat platform-asli ; yangi
'C long
dibaca'; dan kemudianPyLong_FromLong()
dipanggil untuk membungkus ("kotak") asliC long
dalamlong
objek Python (yang, dalam Python 3, yang menghilangkan perbedaan Python 2 antaraint
danlong
, sebenarnya ditampilkan sebagai tipeint
).Tinju ini harus mengalokasikan memori baru untuk
int
objek Python , dan menyemprotkan aslinyaC long
bit asli ke dalamnya. Dalam konteks contoh asli, masa hidup objek ini sangat singkat (hanya cukup lama untuksum()
menambahkan konten ke total berjalan), dan kemudian lebih banyak waktu diperlukan untuk membatalkan alokasi baruint
objek .Di sinilah perbedaan kecepatan berasal, selalu datang, dan selalu akan datang dari dalam implementasi CPython.
sumber
Untuk menambah jawaban sempurna Tim Peters, array mengimplementasikan protokol buffer , sementara daftar tidak. Ini berarti bahwa, jika Anda menulis ekstensi C (atau persamaan moral, seperti menulis modul Cython ), maka Anda dapat mengakses dan bekerja dengan elemen-elemen array jauh lebih cepat daripada apa pun yang bisa dilakukan Python. Ini akan memberi Anda peningkatan kecepatan yang besar, mungkin jauh di atas urutan besarnya. Namun, ada beberapa kelemahan:
Langsung ke ekstensi C mungkin menggunakan palu godam untuk memukul lalat, tergantung pada kasus penggunaan Anda. Anda harus terlebih dahulu menyelidiki NumPy dan melihat apakah itu cukup kuat untuk melakukan matematika apa pun yang Anda coba lakukan. Ini juga akan jauh lebih cepat daripada Python asli, jika digunakan dengan benar.
sumber
Tim Peters menjawab mengapa ini lambat, tetapi mari kita lihat bagaimana memperbaikinya .
Menempel pada contoh Anda
sum(range(...))
(faktor 10 lebih kecil dari contoh Anda agar masuk ke memori di sini):Dengan cara ini juga perlu numpy ke kotak / unbox, yang memiliki overhead tambahan. Untuk membuatnya cepat, seseorang harus tetap dalam kode c numpy:
Jadi dari daftar solusi ke versi numpy ini adalah faktor 16 dalam runtime.
Mari kita periksa juga berapa lama membuat struktur data tersebut
Hapus pemenang: Numpy
Juga perhatikan bahwa membuat struktur data membutuhkan waktu sebanyak penjumlahan, jika tidak lebih. Mengalokasikan memori lambat.
Penggunaan memori dari mereka:
Jadi ini membutuhkan 8 byte per angka dengan berbagai overhead. Untuk range kita menggunakan int 32bit sudah mencukupi, jadi kita bisa mengamankan sebagian memori.
Tapi ternyata menambahkan int 64bit lebih cepat dari int 32bit di mesin saya, jadi ini hanya layak jika Anda dibatasi oleh memori / bandwidth.
sumber
harap dicatat bahwa
100000000
sama dengan10^8
tidak10^7
, dan hasil saya adalah sebagai berikut:sumber