Apa yang lebih efisien dengan Python dalam hal penggunaan memori dan konsumsi CPU - Kamus atau Objek?
Latar belakang: Saya harus memuat data dalam jumlah besar ke Python. Saya membuat sebuah objek yang hanya sebuah wadah bidang. Membuat 4 juta instance dan memasukkannya ke dalam kamus membutuhkan waktu sekitar 10 menit dan ~ 6GB memori. Setelah kamus siap, mengaksesnya dalam sekejap mata.
Contoh: Untuk memeriksa kinerja saya menulis dua program sederhana yang melakukan hal yang sama - satu menggunakan objek, kamus lain:
Objek (waktu eksekusi ~ 18 detik):
class Obj(object):
def __init__(self, i):
self.i = i
self.l = []
all = {}
for i in range(1000000):
all[i] = Obj(i)
Kamus (waktu eksekusi ~ 12 detik):
all = {}
for i in range(1000000):
o = {}
o['i'] = i
o['l'] = []
all[i] = o
Pertanyaan: Apakah saya melakukan sesuatu yang salah atau kamus lebih cepat dari objek? Jika kamus bekerja lebih baik, dapatkah seseorang menjelaskan mengapa?
sumber
Jawaban:
Sudahkah Anda mencoba menggunakan
__slots__
?Dari dokumentasi :
Jadi apakah ini menghemat waktu dan juga memori?
Membandingkan tiga pendekatan di komputer saya:
test_slots.py:
test_obj.py:
test_dict.py:
test_namedtuple.py (didukung di 2.6):
Jalankan benchmark (menggunakan CPython 2.5):
Menggunakan CPython 2.6.2, termasuk tes tuple bernama:
Jadi ya (tidak terlalu mengejutkan), menggunakan
__slots__
adalah pengoptimalan kinerja. Menggunakan tupel bernama memiliki kinerja yang mirip dengan__slots__
.sumber
Akses atribut dalam suatu objek menggunakan akses kamus di belakang layar - jadi dengan menggunakan akses atribut Anda menambahkan biaya tambahan. Ditambah dalam kasus objek, Anda menimbulkan overhead tambahan karena misalnya alokasi memori tambahan dan eksekusi kode (misalnya
__init__
metode).Dalam kode Anda, jika
o
adalah sebuahObj
instance,o.attr
setarao.__dict__['attr']
dengan sejumlah kecil biaya tambahan.sumber
o.__dict__["attr"]
adalah yang memiliki overhead ekstra, mengambil op bytecode ekstra; obj.attr lebih cepat. (Tentu saja akses atribut tidak akan lebih lambat daripada akses berlangganan - ini adalah jalur kode yang sangat penting dan dioptimalkan.)Sudahkah Anda mempertimbangkan untuk menggunakan namaiuple ? ( tautan untuk python 2.4 / 2.5 )
Ini adalah cara standar baru untuk merepresentasikan data terstruktur yang memberi Anda performa tupel dan kenyamanan kelas.
Satu-satunya kelemahan dibandingkan dengan kamus adalah bahwa (seperti tupel) tidak memberi Anda kemampuan untuk mengubah atribut setelah dibuat.
sumber
Ini adalah salinan jawaban @hughdbrown untuk python 3.6.1, saya telah membuat hitungan 5x lebih besar dan menambahkan beberapa kode untuk menguji jejak memori dari proses python di akhir setiap proses.
Sebelum downvoters melakukannya, Harap diperhatikan bahwa metode penghitungan ukuran objek ini tidak akurat.
Dan inilah hasil saya
Kesimpulan saya adalah:
sumber
Hasil:
sumber
Tidak ada pertanyaan.
Anda memiliki data, tanpa atribut lain (tanpa metode, tidak ada). Karenanya Anda memiliki wadah data (dalam hal ini, kamus).
Saya biasanya lebih suka berpikir dalam hal pemodelan data . Jika ada masalah kinerja yang sangat besar, maka saya dapat menyerahkan sesuatu dalam abstraksi, tetapi hanya dengan alasan yang sangat bagus.
Pemrograman adalah tentang mengelola kompleksitas, dan mempertahankan abstraksi yang benar sering kali merupakan salah satu cara paling berguna untuk mencapai hasil tersebut.
Tentang alasan benda lebih lambat, saya pikir pengukuran Anda tidak benar.
Anda melakukan tugas yang terlalu sedikit di dalam perulangan for, dan oleh karena itu yang Anda lihat adalah waktu berbeda yang diperlukan untuk membuat instance dict (objek intrinsik) dan objek "kustom". Meskipun dari segi bahasa keduanya sama, namun penerapannya cukup berbeda.
Setelah itu, waktu penugasan harus hampir sama untuk keduanya, karena pada akhirnya anggota dipertahankan di dalam kamus.
sumber
Ada cara lain untuk mengurangi penggunaan memori jika struktur data tidak seharusnya berisi siklus referensi.
Mari bandingkan dua kelas:
dan
Ini menjadi mungkin karena
structclass
kelas berbasis tidak mendukung pengumpulan sampah siklik, yang tidak diperlukan dalam kasus seperti itu.Ada juga satu keuntungan dari
__slots__
kelas berbasis berlebih : Anda dapat menambahkan atribut tambahan:sumber
Berikut adalah pengujian saya atas skrip yang sangat bagus dari @ Jarrod-Chesney. Sebagai perbandingan, saya juga menjalankannya terhadap python2 dengan "range" diganti dengan "xrange".
Karena penasaran, saya juga menambahkan tes serupa dengan OrderedDict (ordict) untuk perbandingan.
Python 3.6.9:
Python 2.7.15+:
Jadi, pada kedua versi utama, kesimpulan dari @ Jarrod-Chesney masih terlihat bagus.
sumber