Di CPython, elemen daftar disimpan sebagai penunjuk ke elemen, bukan nilai elemen itu sendiri. Ini terbukti dari yang mewakili daftar di C
// Fetched from CPython main branch. Removed comments for brevity. typedef struct { PyObject_VAR_HEAD PyObject **ob_item; /* Pointer reference to the element. */ Py_ssize_t allocated; }PyListObject;
Daftar kosong membangun from sys import getsizeof l = [] print(getsizeof(l)) _3 dan menggunakan sebagian memori
from sys import getsizeof l = [] print(getsizeof(l)) _
Ini kembali
56
Ukuran pasti dari daftar kosong dapat bervariasi di berbagai versi dan implementasi Python
Sebuah penunjuk tunggal ke suatu elemen membutuhkan 8 byte ruang dalam daftar. Setiap kali elemen tambahan ditambahkan ke daftar, Python secara dinamis mengalokasikan memori ekstra untuk mengakomodasi elemen di masa mendatang tanpa mengubah ukuran wadah. Ini menyiratkan, menambahkan satu elemen ke daftar kosong akan mendorong Python untuk mengalokasikan lebih banyak memori dari 8 byte
Mari kita uji ini dan tambahkan beberapa elemen ke daftar
# src.py from sys import getsizeof l = [] l.append(0) print(getsizeof(l)) _
Ini kembali
88
Tunggu, ukuran from sys import getsizeof l = [] print(getsizeof(l)) 4 seharusnya 64 byte (56+8) tetapi malah bertambah menjadi 88 byte. Ini terjadi karena dalam kasus ini, Python mengalokasikan 32 byte ekstra secara berlebihan untuk mengakomodasi elemen yang masuk di masa mendatang. Sekarang, jika Anda menambahkan 3 elemen lagi ke daftar, Anda akan melihat bahwa ukurannya tidak bertambah karena tidak ada alokasi ulang yang terjadi di sini
# src.py from sys import getsizeof l = [] l.append(0) l.append(1) l.append(2) l.append(3) print(getsizeof(l))
Ini mencetak
88
Menambahkan elemen kelima ke daftar di atas akan menambah ukuran daftar sebesar 32 byte (dapat berbeda dalam implementasi lain) lagi
# src.py from sys import getsizeof l = [] for i in range(6): l.append(l) print(getsizeof(l)) _
120
Alokasi memori dinamis ini membuat daftar sangat fleksibel, dan karena daftar hanya menyimpan referensi ke elemen, daftar dapat menampung objek yang heterogen tanpa masalah apa pun. Tetapi fleksibilitas untuk dapat menambahkan sejumlah elemen—tanpa peduli tentang alokasi memori—berakibat pada waktu eksekusi yang lebih lambat
Meskipun biasanya, Anda tidak perlu memikirkan untuk mengoptimalkan ini sama sekali, ada cara yang memungkinkan Anda melakukan pra-alokasi memori statis dalam daftar alih-alih membiarkan Python melakukan alokasi dinamis untuk Anda. Dengan cara ini, Anda dapat memastikan bahwa Python tidak perlu melakukan alokasi memori dinamis berkali-kali saat daftar Anda bertambah
Pra-alokasi statis akan membuat kode Anda sedikit lebih cepat. Saya harus melakukan ini sekali dalam loop bersarang ketat dan peningkatan kinerja 10% signifikan untuk layanan yang sedang saya kerjakan
Pra-alokasi memori dalam daftar
Mari mengukur kinerja menambahkan elemen ke daftar kosong. Saya menggunakan perintah from sys import getsizeof l = [] print(getsizeof(l)) 5 bawaan IPython untuk melakukannya
In [1]: %%timeit ...: ...: l=[] ...: for i in range(10_000): ...: l.append(i) ...: 499 µs ± 1.23 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
Sekarang, jika Anda mengetahui ukuran akhir dari daftar sebelumnya, maka Anda tidak perlu membuat daftar kosong dan menambahkan elemen ke dalamnya melalui sebuah loop. Anda dapat menginisialisasi daftar dengan from sys import getsizeof l = [] print(getsizeof(l)) _6 dan kemudian mengisi elemen seperti ini
from sys import getsizeof l = [] print(getsizeof(l)) _0
Ini sedikit lebih cepat dari cuplikan sebelumnya
from sys import getsizeof l = [] print(getsizeof(l)) _1
Tepung roti
Untuk kasus sederhana yang ditunjukkan di atas, pemahaman daftar akan menjadi sedikit lebih cepat daripada teknik pra-alokasi statis. Lihat diri mu sendiri
from sys import getsizeof l = [] print(getsizeof(l)) _2
Jadi, saya tidak menyarankan melakukan mikro-optimasi tanpa memperlengkapi kode Anda terlebih dahulu. Namun, pra-alokasi daftar masih bisa berguna dalam kasus yang lebih kompleks di mana Anda sudah mengetahui ukuran daftar akhir, dan memangkas beberapa mikrodetik membuat perbedaan yang cukup besar