- Hasil benchmark yang mengukur angka kinerja operasi, memori, dan I/O Python secara sistematis, serta mengkuantifikasi waktu dan penggunaan memori untuk tiap operasi
- Dari sisi kecepatan, disajikan latensi relatif berbagai operasi seperti akses atribut 14ns, penambahan ke list 29ns, membuka file 9μs, respons FastAPI 8.6μs, dan lainnya
- Dari sisi memori, disajikan angka konkret seperti string kosong 41 byte, integer 28 byte, list kosong 56 byte, dictionary kosong 64 byte, dan proses kosong 16MB
- Untuk struktur data, serialisasi, dan pemrosesan asinkron, dibandingkan perbedaan performa antara pustaka standar dan pustaka alternatif seperti
orjson dan msgspec
- Pelajaran utama yang ditekankan mencakup tingginya overhead memori objek Python, cepatnya lookup pada dict/set, efek penghematan memori dari
__slots__, dan perlunya memahami overhead pemrosesan asinkron
Gambaran Umum
- Materi yang merangkum metrik kinerja yang perlu diketahui pengembang Python, dengan menyajikan kecepatan operasi dan penggunaan memori berdasarkan hasil pengukuran nyata
- Benchmark dijalankan pada lingkungan CPython 3.14.2, Mac Mini M4 Pro (ARM, 14-core, RAM 24GB)
- Hasil berfokus pada perbandingan relatif, dan kode serta datanya dibuka di repositori GitHub
Penggunaan Memori (Memory Costs)
- Proses Python kosong menggunakan memori 15.73MB
- String memiliki ukuran dasar 41 byte, dengan tambahan 1 byte per karakter
- Contoh: string kosong 41B, string 100 karakter 141B
- Tipe numerik: integer kecil (0–256) 28B, integer besar (1000) juga 28B, integer sangat besar (10ⁱ⁰⁰) 72B, floating point 24B
- Ukuran dasar koleksi: list 56B, dictionary 64B, set 216B
- Pada 1.000 item: list 35.2KB, dictionary 63.4KB, set 59.6KB
- Instance kelas: kelas biasa (5 atribut) 694B, kelas
__slots__ 212B
- Untuk 1.000 instance: kelas biasa 165.2KB, kelas
__slots__ 79.1KB
Operasi Dasar (Basic Operations)
- Operasi aritmetika: penjumlahan integer 19ns, penjumlahan float 18.4ns, perkalian integer 19.4ns
- Operasi string: konkatenasi 39.1ns, f-string 64.9ns,
.format() 103ns, format % 89.8ns
- Operasi list:
append() 28.7ns, list comprehension (1.000 item) 9.45μs, for loop yang sama 11.9μs
- List comprehension sekitar 26% lebih cepat daripada for loop
Akses dan Iterasi Koleksi (Collection Access and Iteration)
- Akses key/indeks: lookup dictionary 21.9ns, keanggotaan set 19ns, akses indeks list 17.6ns
- Keanggotaan list (1.000 item) memakan 3.85μs, sekitar 200 kali lebih lambat dibanding set/dictionary
- Pemeriksaan panjang:
len() pada list 18.8ns, dictionary 17.6ns, set 18ns
- Iterasi: list (1.000 item) 7.87μs, dictionary 8.74μs,
sum() 1.87μs
Kelas dan Atribut (Class and Object Attributes)
- Kecepatan akses atribut: baik kelas biasa maupun kelas
__slots__ sama-sama 14.1ns untuk baca, dan sekitar 16ns untuk tulis
- Operasi lain: baca
@property 19ns, getattr() 13.8ns, hasattr() 23.8ns
- Dengan
__slots__, efek penghematan memori lebih dari 2 kali lipat, sementara kecepatan akses tetap setara
JSON dan Serialisasi (JSON and Serialization)
- Performa pustaka alternatif dibanding pustaka standar
orjson menserialisasi objek kompleks dalam 310ns, lebih dari 8 kali lebih cepat dibanding json yang membutuhkan 2.65μs
msgspec 445ns, ujson 1.64μs
- Untuk deserialisasi,
orjson juga yang tercepat dengan 839ns
- Pydantic:
model_dump_json() 1.54μs, model_validate_json() 2.99μs
Framework Web (Web Frameworks)
- Dengan respons JSON yang sama: FastAPI 8.63μs, Starlette 8.01μs, Litestar 8.19μs, Flask 16.5μs, Django 18.1μs
- FastAPI memberikan waktu respons sekitar 2 kali lebih cepat daripada Django
File I/O (File I/O)
- Membuka dan menutup file 9.05μs, membaca 1KB 10μs, membaca 1MB 33.6μs
- Menulis: 1KB 35.1μs, 1MB 207μs
- Pickle sekitar 2 kali lebih cepat daripada
json untuk serialisasi maupun deserialisasi (pickle.dumps() 1.3μs, json.dumps() 2.72μs)
Database dan Cache (Database and Persistence)
- SQLite: insert 192μs, select 3.57μs, update 5.22μs
- diskcache: set 23.9μs, get 4.25μs
- MongoDB: insert 119μs, find_one 121μs
- SQLite paling cepat dalam kecepatan baca, sementara diskcache unggul dalam performa tulis
Pemanggilan Fungsi dan Exception (Function and Call Overhead)
- Pemanggilan fungsi: fungsi kosong 22.4ns, method 23.3ns, lambda 19.7ns
- Penanganan exception: try/except (normal) 21.5ns, saat exception terjadi 139ns
- Pemeriksaan tipe:
isinstance() 18.3ns, perbandingan type() 21.8ns
Overhead Async (Async Overhead)
- Pembuatan coroutine 47ns,
run_until_complete 27.6μs
asyncio.sleep(0) 39.4μs, gather(10 coroutines) 55μs
- Dibanding pemanggilan fungsi sinkron (20ns), eksekusi async (28μs) sekitar 1.000 kali lebih lambat
Pelajaran Utama (Key Takeaways)
- Overhead memori objek Python besar; bahkan list kosong pun memakai 56 byte
- Lookup dictionary dan set ratusan kali lebih cepat daripada pencarian pada list
- Pustaka JSON alternatif seperti
orjson dan msgspec 3–8 kali lebih cepat daripada versi standar
- Pemrosesan async memiliki overhead besar, sehingga disarankan hanya saat benar-benar membutuhkan paralelisme
__slots__ dapat memangkas memori hingga kurang dari setengah tanpa hampir ada kehilangan performa
1 komentar
Komentar Hacker News
Banyak orang berkata bahwa jika kita harus peduli pada angka latensi di Python, maka sebaiknya gunakan bahasa lain, tetapi saya tidak setuju.
Basis kode berskala besar seperti Instagram, Dropbox, dan OpenAI juga berkembang dengan Python. Pada akhirnya kita akan menemui masalah performa, dan kemampuan untuk menyelesaikannya di dalam Python tanpa memindahkan semuanya ke bahasa lain itu penting.
Sebagian besar masalah performa bukan berasal dari keterbatasan bahasa, melainkan dari kode yang tidak efisien. Misalnya, loop yang tanpa perlu mengulang pemanggilan fungsi 10 ribu kali.
Python latency quiz yang saya buat juga layak dilihat.
Secara ironis, saat angka-angka seperti ini mulai penting, Python bukan lagi alat yang cocok untuk pekerjaan tersebut.
Dalam praktiknya, yang penting adalah menginstrumentasi kode dan menemukan bottleneck, misalnya dengan alat seperti pyspy. Jika Anda sudah sampai mengkhawatirkan kecepatan menambahkan elemen ke list, maka operasi itu memang seharusnya tidak dilakukan di Python.
Pendekatan seperti ini dimungkinkan berkat interoperabilitas Python dan C. Zig juga makin bagus. Saya mungkin tidak akan menerbangkan pesawat dengan Python, tetapi sense terhadap resource tetap penting.
Mengetahui berapa byte yang dipakai string kosong tidak terlalu berarti. Yang penting adalah memahami kompleksitas waktu dan ruang.
Daripada tahu bahwa int berukuran 28 byte, yang lebih penting adalah menilai apakah program memenuhi kebutuhan performa, dan jika tidak, mencari algoritme yang lebih baik.
Misalnya, fakta bahwa konkatenasi string bersifat O(n²) juga memengaruhi desain f-string di Python.
Karena dictionary cepat, struktur itu juga dipakai luas di seluruh Python.
Angka-angka seperti ini berperan sebagai pembenaran dalam bentuk angka atas pengetahuan implisit tersebut.
Ini mengingatkan saya pada tulisan tentang masalah yang dialami Eric Raymond saat memigrasikan GCC dengan Reposurgeon.
Judulnya membingungkan, padahal sebenarnya ini parodi dari makalah Jeff Dean tahun 2012, “Latency Numbers Every Programmer Should Know”.
Permainan judul seperti ini umum dalam makalah CS.
Itu adalah materi internal untuk desain RAM vs Disk pada mesin pencari Google di masa awal.
Setelah flash memory muncul, angkanya berubah, dan ada juga kisah bahwa Jeff membuat algoritme kompresi untuk langsung menyajikan data genom dari flash.
Kebanyakan pengembang Python sebaiknya fokus pada hal-hal yang lebih penting daripada detail performa tingkat rendah seperti ini.
Materi semacam ini bagus sebagai referensi, tetapi dalam praktiknya jarang benar-benar dibutuhkan.
Penjelasan tentang ukuran string salah. Python memiliki tiga tipe string yang memakai 1, 2, atau 4 byte per karakter.
Untuk detailnya, lihat blog ini.
Judul dan contoh di artikel ini agak kurang akurat.
Misalnya, “item in set 200 kali lebih cepat daripada item in list” sebenarnya berbicara tentang membership test, bukan perbandingan kecepatan iterasi.
Meski begitu, secara keseluruhan format dan susunannya menarik.
Pengukuran waktu pembuatan instance class tidak ada.
Setelah refactor kode, saya pernah mengubah struktur list sederhana menjadi class dan waktu eksekusinya naik dari beberapa mikrodetik menjadi beberapa detik.
Akan bagus jika kasus seperti ini juga diukur.
Mungkin masalahnya adalah penggunaan class yang berlebihan. Kadang struktur list sederhana memang lebih baik.
Kemungkinan yang lebih besar adalah penggunaan paradigma berorientasi objek yang kurang tepat.
Sebaiknya unggah kodenya ke StackOverflow atau CodeReview.SE untuk mendapatkan masukan.
Saya membaca artikel ini dengan sudut pandang, “apakah ada sesuatu yang secara fundamental salah dengan Python modern”.
Tetapi saya tidak setuju dengan klaim bahwa kita harus mengetahui semua angka ini.
Cukup punya intuisi untuk beberapa operasi inti saja.
Rentang small int caching di Python bukan 0~256, melainkan -5~256.
Karena itu, pemula sering bingung dan mencampuradukkan identitas (
is) dengan kesetaraan (==).