Sedikit latar belakang tentang siapa yang menulis ini dan mengapa. Kami adalah Geonode dan kami menjual proxy, sehingga kami selalu berurusan dengan topik ini — web scraping adalah beban kerja I/O-bound yang klasik, dan “scraper saya lambat” adalah salah satu keluhan paling umum yang kami terima. Jadi, inilah penjelasan jujur sebelum membahas teorinya: jika scraper Anda lambat karena mengambil satu halaman sekaligus, membeli proxy tidak akan mempercepatnya. Konkuren adalah sifat dari kode Anda. Seratus titik akhir proxy dan loop sekuensial hanya akan memberi Anda scraper sekuensial dengan seratus titik akhir yang menganggur. Perbaiki masalah koncurrency terlebih dahulu. Proxy memecahkan masalah yang berbeda — yaitu masalah yang Anda hadapi setelah koncurrency berfungsi, ketika target mulai membatasi laju (rate-limiting) alamat yang tiba-tiba mengirimkan lima puluh permintaan per detik. Kedua masalah tersebut nyata. Keduanya bukanlah masalah yang sama dan tidak memiliki solusi yang sama.
Dengan demikian, inilah perbedaannya.
Perbedaan dalam Satu Kalimat
Penjelasan Rob Pike dalam ceramahnya mengenai topik ini tetap yang paling jelas, dan blog Go mengatakannya secara langsung:
Konkuren adalah "komposisi dari proses-proses yang berjalan secara independen."
Paralelisme adalah "eksekusi simultan dari perhitungan (yang mungkin saling terkait)."
Baca kalimat-kalimat tersebut dua kali, karena perbedaannya terletak pada di mana penekanan diletakkan. Konkuren berkaitan dengan struktur — bagaimana Anda memecah suatu masalah menjadi bagian-bagian yang dapat berjalan secara independen. Paralelisme berkaitan dengan eksekusi — berapa banyak bagian tersebut yang secara fisik berjalan pada saat yang sama.
Konsekuensi yang sering terlewatkan: konkurensi adalah sesuatu yang Anda tulis, sedangkan paralelisme adalah sesuatu yang dilakukan oleh mesin. Anda dapat menulis program konkurensi dan menjalankannya pada satu inti prosesor, di mana tidak ada yang pernah berjalan secara bersamaan, namun program tersebut tetap bersifat konkurensi. Anda telah mendeskripsikan tugas-tugas yang independen; runtime-lah yang menyelingi tugas-tugas tersebut. Dan program bersamaan yang dijalankan pada delapan inti mungkin menjadi paralel, jika runtime dan beban kerja memungkinkannya.
Asimetri itulah yang menjadi alasan mengapa kedua kata tersebut tidak dapat digunakan secara bergantian. Bersamaan memungkinkan paralelisme tanpa menjaminnya. Paralelisme tanpa struktur bersamaan sama sekali tidak tersedia.
Mengapa Kebingungan Ini Terus Berlanjut
Ada tiga alasan, dan menyebutkannya akan membantu.
Perilaku yang dapat diamati seringkali identik. Program bersamaan pada satu inti dan program paralel pada empat inti sama-sama tampak seperti "beberapa hal sedang terjadi". Dari luar, Anda tidak dapat membedakan mana yang mana. Anda baru menyadarinya ketika menambahkan inti dan tidak ada perbaikan yang terjadi.
Kosakata setiap bahasa pemrograman tidak konsisten. Modul threading di Python memberikan koncurrency, tetapi secara historis tidak memberikan paralelisme. Modul multiprocessing di Python memberikan keduanya. Fitur async / await di JavaScript memberikan koncurrency, tetapi tidak pernah memberikan paralelisme untuk kode Anda sendiri. Goroutines di Go memberikan koncurrency dan paralelisme hingga GOMAXPROCS. Kata-kata yang sama dalam dokumentasi memiliki arti yang berbeda di berbagai ekosistem.
Sebagian besar waktu Anda tidak perlu peduli, sampai tiba-tiba Anda harus peduli. Untuk beban kerja yang terikat I/O, perbedaannya hampir bersifat akademis — konkurensi saja sudah memberikan keuntungan penuh. Untuk beban kerja yang terikat CPU, hal ini menjadi sangat krusial, karena konkurensi saja tidak memberikan manfaat apa pun. Masalahnya adalah orang-orang mempelajari pola yang berhasil pada masalah yang terikat I/O dan menerapkannya pada masalah yang terikat CPU.
Bagaimana Setiap Bahasa Pemrograman Melakukannya
| Runtime | Mekanisme konkurensi | Paralelisme sejati untuk kode Anda | Di mana sistem ini mengalami kendala |
|---|
| Python (build default) | threading, asyncio | Hanya melalui multiprocessing | GIL membuat eksekusi bytecode menjadi serial |
| Python (build free-threaded) | threading, asyncio | Ya, thread berjalan secara paralel | Overhead single-threaded, kematangan ekosistem |
| Go | goroutines + saluran | Ya, hingga GOMAXPROCS | Status bersama masih memerlukan sinkronisasi |
| Node.js | loop peristiwa, async/await | Hanya melalui worker_threads atau proses anak | Satu callback yang terikat CPU akan memblokir semuanya |
| Java / C# | thread, kumpulan thread | Ya | Kompleksitas status bersama yang dapat diubah |
| Rust | async + thread | Ya | Kompiler memaksa Anda untuk memastikan kebenaran kode terlebih dahulu |
Node.js adalah ilustrasi paling jelas mengenai koncurrency tanpa paralelisme. Dokumentasi resmi menjelaskannya dengan tepat: event loop "memungkinkan Node.js melakukan operasi I/O non-blocking — meskipun secara default hanya menggunakan satu utas JavaScript — dengan mengalihkan operasi ke kernel sistem setiap kali memungkinkan." Kernel bersifat multi-threaded; sedangkan JavaScript Anda tidak. Loop ini berputar melalui enam fase — timer, callback yang tertunda, idle/persiapan, poll, periksa, dan penutupan callback — dan ketika suatu operasi selesai, "kernel memberi tahu Node.js agar callback yang sesuai dapat ditambahkan ke antrian poll untuk akhirnya dieksekusi."
Konsekuensi praktisnya langsung terlihat. Sepuluh ribu permintaan HTTP bersamaan di Node.js bukanlah masalah besar, karena penundaan terjadi di kernel. Satu fungsi yang membebani CPU dan berjalan selama dua detik akan membekukan seluruh proses, karena hanya ada satu thread untuk menjalankannya dan tidak ada yang dapat mendahuluinya.
Go mengambil pendekatan sebaliknya: goroutine cukup murah untuk dibuat dalam jumlah ribuan, dan penjadwal mendistribusikannya ke seluruh thread sistem operasi hingga batas GOMAXPROCS, yang secara default sama dengan jumlah core yang tersedia. Jadi, Go memberikan koncurrency dan paralelisme dari struktur yang sama. Inilah alasan mengapa Pike menyampaikan presentasi tersebut — perbedaan ini sangat penting dalam bahasa pemrograman di mana Anda mendapatkan keduanya dan karenanya dapat membingungkan keduanya.
Pertanyaan Penentu: Apakah Beban Kerja Anda Terbatas pada I/O atau Terbatas pada CPU
Semua hal di atas pada dasarnya bermuara pada satu pertanyaan mengenai beban kerja Anda, dan lebih baik mengukurnya daripada hanya berasumsi.
Terbatas pada I/O berarti program Anda menghabiskan sebagian besar waktunya untuk menunggu: menunggu respons jaringan, pembacaan disk, atau kueri basis data. Selama menunggu, CPU tidak aktif. Konkuren adalah jawaban yang tepat dan memadai, karena konkuren memungkinkan Anda memulai proses menunggu berikutnya sementara proses yang sedang berlangsung masih tertunda. Paralelisme pada dasarnya tidak menambah apa-apa — delapan inti yang menunggu jaringan tidak lebih cepat daripada satu inti yang menunggu jaringan.
CPU-bound berarti program Anda menghabiskan sebagian besar waktunya untuk komputasi: parsing, kompresi, hashing, transformasi. CPU dalam keadaan jenuh. Konkuren saja tidak mengubah apa pun — menyelingi dua komputasi pada satu inti memakan waktu total yang sama dengan menjalankannya secara berurutan, ditambah overhead peralihan. Hanya paralelisme yang membantu, dan hanya hingga jumlah inti fisik yang tersedia.
Untuk mengetahui kondisi mana yang Anda hadapi, lakukan pengukuran daripada hanya berasumsi. Di Linux, perintah time akan langsung memberikan jawabannya: bandingkan waktu yang sebenarnya berlalu dengan total waktu CPU pengguna ditambah sistem. Jika waktu nyata jauh lebih besar daripada waktu CPU, Anda sedang menunggu — terikat I/O. Jika keduanya hampir sama, Anda sedang melakukan perhitungan — terikat CPU.
Pengikisan web (web scraping) merupakan contoh yang berguna karena melibatkan kedua kondisi tersebut secara berurutan. Mengambil halaman sangat terikat I/O; sedangkan mengurai HTML setelahnya terikat CPU. Arsitektur yang tepat menggunakan koncurrency untuk pengambilan halaman dan paralelisme untuk penguraian, sedangkan kesalahan umum adalah menerapkan satu strategi untuk kedua tahap tersebut. Sebuah scraper dengan 200 pengambilan halaman secara bersamaan yang memasok data ke parser ber-thread tunggal bukanlah scraper yang cepat — melainkan pengambil halaman yang cepat dengan antrian yang menumpuk di belakangnya.
GIL di Python dan Perubahan yang Dibawa oleh Free Threading
Python layak mendapatkan bagian tersendiri karena situasinya benar-benar telah berubah, dan banyak informasi yang akan Anda baca mengenai hal ini kini sudah ketinggalan zaman.
Secara historis: Global Interpreter Lock (GIL) pada CPython hanya mengizinkan satu thread untuk mengeksekusi bytecode Python pada satu waktu. Oleh karena itu, threading memberikan koncurrency tetapi bukan paralelisme. GIL dilepaskan selama operasi I/O, sehingga I/O berbasis thread berjalan dengan baik; namun, threading yang bergantung pada CPU tidak berfungsi, dan penggunaan multiprocessing menjadi solusi sementara.
Perubahan yang terjadi: mulai dari rilis 3.13, CPython menyediakan build opsional dengan GIL dinonaktifkan. Dokumentasi free-threading menjelaskannya dengan jelas — "Eksekusi free-threaded memungkinkan pemanfaatan penuh daya pemrosesan yang tersedia dengan menjalankan thread secara paralel pada inti CPU yang tersedia."
PEP 779, yang disetujui oleh Dewan Pengarah pada 16 Juni 2025 dengan status Final, menetapkan kriteria untuk memindahkan free-threading dari status eksperimental ke status yang didukung secara resmi, dengan menargetkan Python 3.14 untuk fase tersebut.
Empat hal praktis sebelum Anda menggunakannya:
Ini bukan build default. Anda harus memperoleh atau membangunnya secara sengaja — dari sumber, artinya menggunakan opsi konfigurasi --disable-gil. Periksa apa yang sedang Anda jalankan dengan perintah python -VV, yang menampilkan "free-threading build", atau sys._is_gil_enabled(), yang mengembalikan False saat GIL dinonaktifkan.
Kode single-threaded menjadi lebih lambat. Dokumentasi melaporkan bahwa pada rangkaian pengujian pyperformance "overhead rata-rata berkisar antara sekitar 1% pada macOS aarch64 hingga 8% pada sistem Linux x86-64". PEP 779 mencatat bahwa Dewan Pengarah memperkirakan Python free-threaded "akan sekitar 10-15% lebih lambat", dengan 15% sebagai target pasti untuk fase II, dan menerima peningkatan rata-rata geometris sebesar 20% dalam penggunaan memori sebagai "biaya untuk memiliki free-threading yang efisien dan aman". Jika program Anda bersifat single-threaded, build ini merupakan regresi langsung.
Anda dapat mengaktifkan kembali GIL saat runtime. Build free-threaded mendukung eksekusi dengan GIL diaktifkan melalui variabel lingkungan PYTHON_GIL atau opsi -X gil — berguna ketika suatu dependensi berperilaku tidak semestinya.
Dependensi Anda adalah batasan utamanya. Ekstensi C harus dikompilasi untuk mendeklarasikan dukungan free-threading. Ekosistem telah berkembang pesat, tetapi “berfungsi di mesin saya dengan Python murni” tidak sama dengan “stack ilmiah saya berfungsi”.
Untuk kasus yang dibatasi oleh I/O yang mendominasi pekerjaan scraping dan API, semua ini tidak mengubah keputusan Anda: asyncio atau thread pool pada build standar sudah memberikan semua yang dapat diberikan oleh koncurrency. Free threading penting ketika tahap parsing, bukan tahap pengambilan data, menjadi bottleneck Anda.
Contoh Perhitungan: Mengambil 10.000 URL
Angka konkret membuat perbedaannya jelas. Asumsikan setiap permintaan memakan waktu 200 ms dan mengurai setiap respons membutuhkan 50 ms waktu CPU, pada mesin dengan empat inti.
Secara berurutan. 10.000 × 250 ms = 2.500 detik, sekitar 42 menit. CPU menganggur selama 80% dari waktu tersebut.
Pengambilan secara bersamaan, penguraian secara berurutan. Dengan 100 permintaan secara bersamaan, waktu pengambilan berkurang menjadi sekitar 20 detik waktu nyata. Waktu penguraian tetap sama: 10.000 × 50 ms = 500 detik. Total sekitar 520 detik, kira-kira 9 menit. Peningkatan sebesar 4,8× — dan perhatikan ke mana waktu tersebut hilang. Waktu pengambilan data sebelumnya mencapai 80% dari waktu eksekusi asli dan kini menjadi 4% dari waktu eksekusi baru. Pemrosesan, yang tidak Anda ubah, kini mencakup 96% dari keseluruhan waktu.
Pengambilan data secara bersamaan, pemrosesan data secara paralel pada empat inti prosesor. Waktu pemrosesan data turun menjadi sekitar 125 detik. Total sekitar 145 detik, kira-kira 2,5 menit. Peningkatan 17 kali lipat dibandingkan dengan metode sekuensial.
Tiga pelajaran tersirat dari angka-angka tersebut.
Pertama, keuntungan terbesar berasal dari memperbaiki I/O dengan koncurrency, dan hal ini hampir tanpa biaya — tanpa inti tambahan, tanpa masalah shared-state, hanya loop yang berbeda.
Kedua, begitu Anda memperbaiki bottleneck dominan, bottleneck berikutnya langsung menjadi dominan. Inilah Hukum Amdahl dalam bentuknya yang paling praktis: mengoptimalkan tahap yang memakan 20% dari waktu eksekusi tidak akan membuat Anda lebih cepat dari 25%, tidak peduli seberapa sempurna Anda menghilangkannya. Selalu ukur sebelum mengoptimalkan, dan ukur lagi setelahnya, karena hasilnya akan berubah.
Ketiga — dan di sinilah kepentingan komersial kita menjadi relevan, jadi pertimbangkanlah dengan tepat — begitu Anda beralih dari satu permintaan sekaligus menjadi seratus, Anda akan terdeteksi. Sebuah alamat tunggal yang melakukan 500 permintaan per detik ke satu host akan dibatasi laju (rate-limited), lalu diblokir. Itu bukanlah masalah konkurensi dan tidak ada solusi “asyncio” yang dapat mengatasinya; ini adalah masalah distribusi, dan itulah fungsi dari proxy. Tarif lalu lintas residensial kami mulai dari $0,79/GB dan pusat data mulai dari $0,14/GB, sesuai dengan halaman harga kami pada September 2026. Namun, perhatikan urutannya: prioritaskan koncurrency terlebih dahulu, baru gunakan proxy ketika koncurrency menimbulkan masalah yang tidak dapat diselesaikannya. Melakukannya dengan urutan terbalik berarti Anda harus membayar bandwidth yang tidak mungkin Anda gunakan.
Saat Konkuren Si Tidak Lagi Bermanfaat
Menambah tingkat konkuren akan menghasilkan manfaat yang semakin berkurang, bahkan akhirnya menjadi merugikan, dan titik baliknya tiba lebih cepat daripada yang diperkirakan kebanyakan orang.
Batas koneksi. Sistem operasi membatasi jumlah deskriptor file yang terbuka. Server membatasi jumlah koneksi bersamaan per klien. Sepuluh ribu permintaan bersamaan dari satu mesin akan mencapai salah satu batas ini jauh sebelum mencapai batas CPU, dan mode kegagalan biasanya berupa kesalahan yang membingungkan daripada yang jelas.
Memori. Setiap permintaan yang sedang diproses menyimpan buffer, header yang telah diparsing, dan data respons yang tertunda. Sepuluh ribu permintaan bersamaan yang masing-masing menyimpan 100 KB berarti satu gigabyte memori yang hanya menunggu tanpa melakukan apa-apa.
Overhead peralihan konteks. Thread sistem operasi tidak gratis — masing-masing membawa tumpukan (stack) dan biaya penjadwalan. Inilah tepatnya mengapa goroutine dan coroutine ada: keduanya cukup efisien sehingga ribuan goroutine atau coroutine masih masuk akal, sedangkan ribuan thread sistem operasi tidak.
Toleransi target. Ujung lain dari koneksi memiliki batasan. Melampaui batas tertentu, koncurrency tambahan akan menghasilkan kode status 429 dan 503 alih-alih data, dan throughput efektif Anda justru menurun seiring penambahan koncurrency. Inilah batas atas yang paling umum di dunia nyata dan yang paling jarang diukur, karena permintaan tersebut masih "berfungsi" — hanya saja mereka mengembalikan kesalahan yang dengan patuh diulang oleh loop percobaan ulang.
Pendekatan praktisnya tidak terlalu menarik: mulailah dengan batas koncurrency yang wajar, ukur jumlah permintaan yang berhasil diselesaikan per detik daripada jumlah permintaan yang dicoba, dan tingkatkan hingga throughput berhenti meningkat. Angka tersebut akan mencapai titik jenuh, lalu menurun. Titik optimal berada di titik jenuh tersebut, dan biasanya angkanya jauh lebih kecil daripada yang diperkirakan secara intuitif — seringkali puluhan, bukan ratusan.
Saat Anda Tidak Membutuhkan Keduanya
Perlu ditekankan, karena ungkapan "buatlah menjadi bersamaan" sudah menjadi kebiasaan.
Ketika pekerjaan tersebut benar-benar kecil. Seratus permintaan yang masing-masing memakan waktu 200 ms akan memakan waktu 20 detik jika dijalankan secara berurutan. Jika dijalankan setiap malam melalui cron job, 20 detik sudah cukup, dan kode yang berjalan secara bersamaan lebih sulit untuk didebug ketika terjadi kegagalan pada pukul 3 pagi.
Ketika urutan merupakan bagian dari persyaratan. Beberapa alur kerja harus memproses item dalam urutan yang ketat, atau setiap langkah bergantung pada hasil langkah sebelumnya. Konkuren di sini bukan hanya tidak membantu; melainkan sumber bug yang hanya muncul saat beban tinggi.
Ketika titik leher botol berada di tempat lain sama sekali. Jika penulisan ke database menjadi kendala, 200 pembaca bersamaan hanya akan membentuk antrian yang lebih panjang di depan kunci yang sama. Perbaiki leher botol yang sebenarnya. Konkuren di hulu sumber daya serial mengubah program lambat menjadi program lambat dengan masalah memori.
Ketika keadaan bersama (shared state) rumit. Kode bersamaan yang menyentuh keadaan bersama yang dapat diubah memerlukan sinkronisasi, dan kesalahan dalam melakukannya akan menghasilkan jenis bug terburuk — bug yang muncul secara sporadis, bergantung pada beban sistem, dan tidak dapat direproduksi di mesin Anda. Jika peningkatan kecepatan hanya 2× dan keadaannya rumit, kode sekuensial yang dapat Anda pahami seringkali merupakan keputusan teknik yang lebih baik.
Dan versi yang relevan bagi kita: jika Anda mengikis beberapa ratus halaman sehari dari situs yang tidak mempermasalahkannya, Anda tidak memerlukan koncurrency maupun proxy. Satu panggilan requests dalam loop dengan penundaan yang sopan adalah jawaban yang tepat, dan kami lebih memilih memberi tahu Anda hal itu daripada menjual paket yang tidak Anda butuhkan.
Pertanyaan Lainnya
Apa perbedaan paling sederhana antara konkurensi dan paralelisme?
Konkurensi adalah menangani banyak hal sekaligus — suatu sifat struktural dari cara Anda menulis program. Paralelisme adalah melakukan banyak hal sekaligus — suatu sifat fisik dari cara program tersebut dijalankan. Konkuren memungkinkan terjadinya paralelisme; namun, konkuren tidak secara langsung menyebabkan terjadinya paralelisme.
Apakah mungkin ada paralelisme tanpa konkuren?
Tidak secara berguna dalam konteks yang dibahas di sini. Eksekusi paralel memerlukan unit kerja yang independen untuk didistribusikan, dan mendefinisikan unit-unit tersebutlah yang dimaksud dengan konkurensi. Paralelisme tingkat perangkat keras seperti SIMD merupakan pengecualian — ia memaralelkan aliran instruksi tunggal atas data tanpa adanya struktur konkurensi dalam program Anda.
Apakah GIL Python masih ada?
Ya, pada build default. Sejak versi 3.13, CPython juga menyediakan build opsional dengan thread bebas (free-threaded) yang menonaktifkan GIL, dan PEP 779 telah mengarahkan build tersebut menuju status dukungan resmi yang ditargetkan untuk versi 3.14. Build default masih memilikinya, jadi kecuali Anda secara sengaja menginstal interpreter dengan thread bebas, GIL tetap ada.
Apakah async sama dengan multithreading?
Tidak. Konkuren async menggunakan satu utas dengan peralihan kooperatif pada titik-titik await yang eksplisit, sehingga hanya satu bagian kode Anda yang berjalan pada satu waktu dan peralihan hanya terjadi di tempat yang Anda tentukan. Multithreading menggunakan beberapa thread sistem operasi dengan peralihan preemptif yang dapat terjadi di mana saja. Async lebih mudah dipahami; thread dapat mencapai paralelisme sejati di mana runtime mengizinkannya.
Berapa banyak permintaan bersamaan yang sebaiknya saya buat?
Lebih sedikit dari yang Anda kira. Mulailah sekitar 10, ukur jumlah permintaan yang selesai per detik, dan tingkatkan hingga angka tersebut berhenti naik. Batas atasnya biasanya ditentukan oleh toleransi server tujuan, bukan kapasitas mesin Anda, dan melampaui titik tersebut, koncurrency tambahan justru menimbulkan kesalahan alih-alih meningkatkan throughput.
Apakah koncurrency membuat kode saya lebih cepat?
Hanya jika Anda sedang menunggu sesuatu. Untuk pekerjaan yang terikat I/O, keuntungannya besar. Untuk pekerjaan yang terikat CPU pada satu inti, koncurrency justru membuat proses sedikit lebih lambat karena overhead peralihan — Anda membutuhkan paralelisme, yang berarti memerlukan beberapa inti dan runtime yang dapat memanfaatkannya.
Apa perbedaan antara multiprocessing dan multithreading?
Thread berbagi memori dalam satu proses, yang membuat komunikasi menjadi murah namun berbagi status menjadi berbahaya. Proses memiliki memori terpisah, yang membuatnya aman namun komunikasi menjadi mahal. Pada Python versi standar, proses adalah cara untuk mendapatkan paralelisme sejati pada pekerjaan yang dibatasi oleh CPU; thread memberikan koncurrency untuk pekerjaan yang dibatasi oleh I/O.
Apakah saya memerlukan proxy untuk menjalankan permintaan secara bersamaan?
Tidak secara inheren. Anda membutuhkannya ketika koncurrency membuat Anda cukup terlihat sehingga target membatasi laju (rate-limit) atau memblokir alamat asal Anda. Terhadap API dengan kuota yang besar, atau situs yang Anda memiliki izin untuk di-crawl, koncurrency saja sudah cukup. Terhadap situs yang membatasi per alamat, distribusi menjadi kendala — dan itu adalah pembelian terpisah dari memperbaiki kode Anda.
Kesimpulan
Perbedaan ini patut diperhatikan karena mengubah pertanyaan yang samar — "bagaimana cara mempercepat ini?" — menjadi pertanyaan yang spesifik dengan jawaban yang dapat diuji: apakah saya sedang menunggu, ataukah sedang melakukan perhitungan?
Jika Anda sedang menunggu, Anda memerlukan koncurrency, dan Anda membutuhkannya dalam bentuk apa pun yang disediakan oleh bahasa pemrograman Anda. Keuntungannya besar, biasanya tidak memerlukan perangkat keras tambahan, dan tersedia di setiap runtime utama. Jika Anda sedang memproses data, koncurrency saja tidak akan membantu, dan Anda memerlukan paralelisme sejati — proses, thread pekerja, goroutine di seluruh inti prosesor, atau dalam kasus Python, mungkin interpreter dengan thread bebas yang memiliki trade-off tersendiri yang perlu dipertimbangkan.
Sebagian besar program nyata mengalami kedua kondisi tersebut secara bertahap, dan urutan prosesnya lebih penting daripada pilihan teknologinya. Perbaiki leher botol yang dominan, ukur kembali, dan harapkan hasilnya telah berubah. Sebuah pipeline yang semula 80% terkendala jaringan akan menjadi 96% terkendala parsing begitu Anda memperbaiki masalah jaringan, dan optimasi kedua ini merupakan pekerjaan yang sama sekali berbeda dari yang pertama.