Throughput per siklus CPU: angka yang bertahan setelah bersentuhan dengan router sungguhan
Angka lab dan angka perangkat keras nyaris tidak pernah sepakat. Kami mengukur keduanya, menjaga rentangnya, mengungkap jebakan yang pernah menghasilkan satu angka yang salah, dan mengatakan dengan jujur di mana kami kalah.
Dipublikasikan 6 September 2026
Pada dua router fisik, memindahkan lalu lintas sungguhan lewat LAN gigabit sungguhan, inti terowongan kami mencapai 122 Mbit/s, sementara implementasi tunnel kernel bawaan router mencapai 66.8 Mbit/s — pada pengirim yang sama, dengan CPU lebih sedikit per Mbit. Tidak ada satu pun angka yang menceritakan keseluruhan kisahnya sendirian: kedua kubu terpaksa memakai generator lalu lintas yang berbeda, karena tidak ada satu generator pun yang berjalan di kedua sisi perbandingan itu. Jadi rasio yang layak diperdebatkan adalah masing-masing kubu dibandingkan dengan batas atas tanpa-terowongannya sendiri, yang ada di tabel di bawah dan justru menguntungkan kami lebih besar, bukan lebih kecil. Uji lab empat-VM di sebuah laptop menunjukkan angka yang lebih cepat secara absolut dan, pada satu sisi, sesuatu yang kurang menguntungkan. Kami lebih memercayai angka dari router, karena router adalah satu-satunya di antara keduanya yang benar-benar dilewati lalu lintas pelanggan.
Itulah temuan yang layak dijadikan pembuka, dan juga alasan mengapa tulisan ini memiliki dua bagian hasil, bukan satu: lab dan perangkat keras tidak sepakat soal sisi mana yang lebih penting, dan menutupi ketidaksepakatan itu akan menghabiskan lebih banyak kredibilitas daripada keuntungan yang didapat dari angka mana pun.
Baca ini sebelum melihat angka-angkanya. Data plane kami secara bawaan memakai satu thread. Pembanding yang kami jalankan bersamanya di lab secara bawaan memakai empat. Jika dibiarkan tidak disetarakan, satu flag itu saja mengubah throughput-nya dari sekitar 789 menjadi 1427 Mbit/s — sebuah kesenjangan pada pengaturan bawaan, bukan pada datapath-nya — dan hal ini pernah menghasilkan satu angka yang salah dipublikasikan di sini. Setiap angka lab di bawah ini mengunci kedua inti Rust pada satu thread. Implementasi Go dan kernel pada tabel itu berjalan dengan pengaturan bawaan masing-masing, yang memakai lebih dari satu — jika ada bedanya, itu justru menguntungkan mereka dibandingkan dengan thread tunggal kami. Kesenjangan bawaan-sebagaimana-dirilis ditampilkan terpisah, satu kali, agar tidak tersembunyi karena kelalaian juga.
Apa yang diukur, dan pada apa
Dua lingkungan independen, karena sebuah benchmark hanya jujur dalam lingkungan tempatnya
dijalankan — angka iperf3 dan angka bandwidth-test bukanlah satu angka yang memakai dua nama, dan
VM yang berbagi core laptop bukanlah router.
Perangkat keras — dua router MikroTik, satu LAN gigabit, 2026-09-01. RB3011UiAS (ARMv7, 2×1400
MHz) sebagai pengirim, cAP ax (ARM64, 4×864 MHz) sebagai penerima, RouterOS 7.24.1 pada keduanya.
Inti kami berjalan sebagai fv, di dalam container RouterOS, di userspace, melawan implementasi
protokol WireGuard bawaan kernel router itu sendiri — satu-satunya pembanding yang setara
(apples-to-apples) yang tersedia pada perangkat keras tersebut, karena itulah satu-satunya alternatif
yang tersedia langsung di RouterOS. Dua generator, karena tidak ada satu pun yang melintasi
implementasi dengan bersih: dd | nc untuk uji container kami, bandwidth-test bawaan RouterOS untuk
uji kernel — masing-masing membawa batas atas tanpa-terowongannya sendiri sebagai kontrol, sehingga
pembaca bisa memeriksa kewajaran batas itu sebelum memercayai rasionya.
Lab — empat guest Debian aarch64 di bawah QEMU/HVF pada satu host Apple-silicon, 2026-08-31.
Keypair, alamat, port, MTU, dan proses bring-up interface yang sama untuk setiap binari yang diuji:
inti kami, boringtun 0.7 (inti userspace tempat inti kami di-fork), wireguard-go (implementasi
referensi protokol WireGuard, userspace), dan modul protokol WireGuard bawaan kernel Linux sendiri.
Uji iperf3 selama 15 detik, tiga sampel masing-masing, kedua inti Rust dikunci pada satu thread data
plane karena alasan pada catatan di atas.
task test:impls # lab: ours vs boringtun vs wireguard-go vs the kernel
task test:all # lab: single-hop, multi-hop, and the comparisonUji router belum menjadi target task — pengujiannya adalah fv yang dijalankan manual di dalam
container RouterOS pada setiap kotak, melawan server protokol WireGuard bawaan RouterOS. Seluruh
topologi dan setiap baris data mentah ada di docs/benchmarks/results.csv milik kami sendiri.
Angka yang benar-benar penting: di router
| Ukuran | inti kami (userspace) | tunnel kernel bawaan router |
|---|---|---|
| throughput | 122 Mbit/s | 66.8 Mbit/s |
| CPU, pengirim | 65% (63–67) | 74% (70–79) |
| CPU, penerima | 18% (10–22) | 20% (18–21) |
| CPU per Mbit | 0.53 %/Mbit | 1.10 %/Mbit |
| RTT | 1.22 ms | 0.92 ms |
| memori resident (pengirim / penerima) | 3.0 / 3.1 MB | di dalam kernel, tidak bisa dipisahkan |
| generator | dd | nc | bandwidth-test |
| batas atas tanpa-terowongan generator itu | 187 Mbit/s | 220.5 Mbit/s |
| yang dipertahankan dari batas atasnya sendiri | 65% | 30% |
Panjang batang ∝ Mbit/s, sumbu 0–140. Pengirim RB3011 yang sama pada kedua uji; CPU-nya menjadi batas sepanjang waktu, 65–79%, sementara cAP ax penerima nyaris menganggur di sekitar 20%.
Dua kerangka independen sepakat, dan itulah yang membuat hasilnya bisa dipercaya, bukan sekadar dipilih-pilih: throughput absolut menguntungkan inti kami 1.8×, dan throughput yang dipertahankan terhadap batas atas tanpa-terowongan masing-masing generator menguntungkan kami 2.2×. Yang tidak dikatakan tabel: kedua generator bukanlah alat yang sama, itulah sebabnya kedua batas atas-tanpa-terowongan dicetak, bukan satu rasio headline, dan kedua angka itu akan berubah jika pengirim menerima lalu lintas jaringan sungguhan alih-alih membangkitkannya secara lokal. Berbeda dengan tabel lab di bawah, ini adalah uji tunggal: persentase dalam kurung adalah rentang pergerakan CPU selama uji itu, bukan sebaran dari uji yang diulang-ulang.
Lab: tiga inti userspace, satu kernel, thread yang disetarakan
| inti | jenis | TCP Mbit/s (rentang, n=3) | memori puncak | latensi p99 |
|---|---|---|---|---|
| milik kami, 1 thread | userspace, Rust | 1535 (1506–1554) | 3.5 MB | 0.94 ms |
| boringtun, 1 thread | userspace, Rust | 1427 (1346–1479) | 5.3 MB | 1.15 ms |
| wireguard-go, bawaan | userspace, Go | 1228 (1221–1239) | 34.8 MB | 1.39 ms |
| WireGuard kernel, bawaan | kernel, C | 1376 (1366–1386) | — (kernel, tidak bisa dipisahkan) | 0.96 ms |
Sebagaimana dirilis, tanpa disetarakan: inti kami secara bawaan memakai 1 thread dan mencapai 1535 Mbit/s; boringtun secara bawaan memakai 4 thread dan mencapai sekitar 789 Mbit/s pada pengaturan itu. Melaporkan hanya angka bawaan-lawan-bawaan akan mengulang kesalahan yang sama, kali ini ke arah yang menguntungkan. Kedua angka itu nyata; hanya satu di antaranya yang benar-benar memisahkan datapath dari jumlah thread.
Biaya CPU adalah tempat lab ini benar-benar berguna dalam tulisan ini — itulah satu-satunya sisi yang tidak bisa ditunjukkan tabel router, karena pembanding di dalam kernel milik router tidak memberi rincian CPU apa pun untuk dibandingkan.
Semakin rendah semakin baik. Sumbu 0–2.5 s/Gbit, linear. Kedua segmen ditumpuk dari kiri ke kanan; pemisahannya adalah tempat kerugian pada bagian berikutnya muncul.
Skala log, 1–50 MB, dinyatakan secara eksplisit karena itu bukan pengaturan bawaan yang jujur: pada sumbu linear, 34.8 MB milik wireguard-go akan meratakan kedua batang lain menjadi garis tipis.
Di mana kami tidak menang
Dua tempat, keduanya dijelaskan, bukan dikubur di dalam CSV:
CPU mode-user, di lab. 0.347 s/Gbit berbanding 0.290 milik pembanding Rust yang lebih murah — satu-satunya kolom pada tabel itu tempat kami tertinggal. Menurut pembacaan kami, ini adalah sesuatu yang "dibeli", bukan disia-siakan: inti kami melakukan offload checksum TUN di kedua arah, yang memindahkan pekerjaan keluar dari kernel dan masuk ke CPU userspace yang dihitung sebagai waktu user — dan kolom CPU-system, tempat kompromi itu dibayar kembali, justru jauh lebih unggul (0.549 berbanding 0.787). Total CPU tetap menang. Pemisahan user/sys di atas ditampilkan justru agar kompromi ini terlihat, bukan sekadar diklaim.
Round-trip time, di router. 1.22 ms berbanding 0.92 ms milik tunnel kernel — satu-satunya sisi perangkat keras tempat implementasi di dalam kernel menang telak, karena alasan yang wajar: jalur kami membayar biaya satu hop veth container dan satu giliran penjadwalan userspace yang tidak dibayar oleh jalur di dalam kernel. Menariknya, lab justru menunjukkan urutan sebaliknya pada latensi p99 (0.94 ms berbanding 0.96 ms milik kernel) — kami tidak menyimpulkan apa pun dari itu selain "lingkungan berbeda, hambatan berbeda", dan kami mengatakannya secara jujur alih-alih memilih angka mana pun yang lebih menguntungkan.
Apa yang baru dan apa yang diwariskan
Setiap angka di atas membawa tanggal, dan tanggal itu penting: tabel router adalah 2026-09-01, tabel
lab adalah 2026-08-31. Sejak itu, sebuah pembaruan versi mayor yang terkoordinasi memindahkan crate
kripto yang dipanggil data plane kami di setiap paket — chacha20poly1305, blake2,
curve25519-dalek — dan kami belum menjalankan ulang perbandingan lab atau router secara penuh
terhadap tree itu. Angka-angka di atas nyata dan diukur dengan cermat, tetapi usianya sudah enam hari
sejak dipublikasikan, pada tree yang mendahului pembaruan itu. Kami mempublikasikannya dengan
menyatakan hal itu secara terbuka, bukan diam-diam, dan kami akan memberi tanggal pada pengujian ulang
begitu kami memilikinya. Angka router juga tidak dipatok ke commit tertentu dalam catatan kami sendiri
— baca "2026-09-01" sebagai "awal September", bukan terkait pada satu perubahan optimisasi tertentu.
Yang bisa kami jalankan ulang hari ini, di mesin ini, tanpa router atau lab VM, adalah bagian jalur yang paling langsung disentuh oleh pembaruan kripto itu: biaya encapsulate dan decapsulate, terpisah dari setiap urusan socket, TUN, dan threading di atas.
| operasi, ukuran paket | inti kami | boringtun 0.7.1 |
|---|---|---|
| encapsulate, 64 B | 219.2 ns | 218.7 ns |
| encapsulate, 512 B | 436.4 ns | 442.5 ns |
| encapsulate, 1420 B | 938.1 ns | 968.3 ns |
| decapsulate, 64 B | 238.8 ns | 226.7 ns |
| decapsulate, 512 B | 562.1 ns | 539.2 ns |
| decapsulate, 1420 B | 1099.5 ns | 1069.4 ns |
Diuji ulang 2026-09-05, Apple M3 Max,
cargo bench -p forestvpn-quantum --bench datapath_benches -- --measurement-time 3 --warm-up-time 1,
criterion 0.8.2, 100 sampel per titik. Tidak ada socket, tidak ada TUN, tidak ada thread — satu
handshake yang selesai, lalu encapsulate/decapsulate dalam sebuah loop. Hasilnya bercampur menurut
arah dan ukuran paket, semuanya dalam kisaran beberapa persen ke arah mana pun, persis seperti yang
kami perkirakan: inti kami di-fork dari jalur ini dari upstream-nya, dan keduanya kini berada pada
generasi primitif RustCrypto yang sama. Ini bukan klaim throughput — ini menyatakan bahwa jalur
kripto hasil fork itu sendiri berbiaya kurang lebih sama dengan milik upstream-nya, dalam batas noise,
dan itulah yang membuat kemenangan lab dan router di atas layak dipercaya: kemenangan itu datang dari
bagian lain pada stack (batching syscall, offload, threading), bukan dari cipher yang lebih cepat.
Mereproduksi
task test:impls # lab: ours vs boringtun vs wireguard-go vs the kernel
task test:all # lab: single-hop, multi-hop, and the multihop cost table
cargo bench -p forestvpn-quantum --bench datapath_benches -- --measurement-time 3 --warm-up-time 1Uji router masih manual hari ini: fv di dalam container RouterOS pada setiap kotak, melawan server
protokol WireGuard bawaan RouterOS.
Agar jujur soal apa yang sebenarnya didapat dari ini: harness dan results.csv mentah di balik setiap
tabel di sini ada di tree kami sendiri, yang belum publik hari ini. Baca perintah-perintah di atas
sebagai pernyataan yang persis tentang apa yang kami jalankan, bukan sebagai sesuatu yang bisa Anda
clone dan ulangi. Klaim yang paling tidak bergantung pada kami juga adalah yang paling layak diperiksa
— boringtun dan wireguard-go bersifat publik, iperf3 bersifat publik, dan jebakan jumlah-thread
di awal tulisan ini (satu flag yang menggeser sebuah pembanding antara sekitar 789 dan 1427 Mbit/s
pada satu host) bisa direproduksi oleh siapa pun yang memiliki ketiganya tanpa satu pun kode kami di
dalamnya. Jika itu tetap berlaku bagi Anda, baris-baris yang sudah disetarakan di atas adalah yang
layak diperdebatkan. Jika tidak, kami ingin mendengarnya.