CPU çevrimi başına aktarım hızı: bir yönlendiriciyle karşılaşmayı atlatan sayı
Bir laboratuvar sayısı ile bir donanım sayısı neredeyse hiç uyuşmaz. İkisini de ölçtük, aralıkları koruduk, bir kez yanlış sonuç üreten tuzağı açıkladık ve nerede kaybettiğimizi açıkça söyledik.
6 Eylül 2026 tarihinde yayımlandı
İki fiziksel yönlendiricide, gerçek bir gigabit LAN üzerinden gerçek trafik taşırken, tünel çekirdeğimiz 122 Mbit/s ile itti; yönlendiricinin kendi çekirdek tünel uygulaması ise 66.8 Mbit/s ile — aynı gönderici üzerinde, Mbit başına daha az CPU ile. Hiçbir rakam tek başına hikâyenin tamamı değildir: iki kolun farklı trafik üreticileri kullanması gerekti, çünkü tek bir üretici bu karşılaştırmanın iki tarafında da çalışmaz. Bu yüzden tartışılacak oran, her kolun kendi tünelsiz tavanına karşı olan oranıdır; bu, aşağıdaki tabloda yer alır ve bizi daha da lehimize çevirir, aleyhimize değil. Bir dizüstü bilgisayarda dört VM'lik bir laboratuvar çalıştırması, mutlak rakamlarla daha hızlı bir şey ve bir eksende daha az göz alıcı bir şey söylüyor. Yönlendirici rakamına daha çok güveniyoruz, çünkü ikisinden yalnızca yönlendirici, bir müşterinin trafiğinin fiilen geçtiği yerdir.
Öne çıkarılmaya değer bulgu budur ve bu yazının bir yerine iki sonuç bölümü olmasının da nedeni budur: laboratuvar ile donanım, hangi eksenin önemli olduğu konusunda anlaşmıyor ve bu anlaşmazlığın üzerini örtmek, her iki rakamın kazandırdığından daha fazla güvenilirliğe mal olurdu.
Rakamlardan önce bunu okuyun. Veri düzlemimiz varsayılan olarak tek iş parçacığı kullanır. Laboratuvarda yanında çalıştırdığımız karşılaştırma öğesi ise varsayılan olarak dört kullanır. Eşitlenmeden bırakıldığında, yalnızca bu bayrak aktarım hızını ~789'dan 1427 Mbit/s'ye kadar sallar — bu bir varsayılan farkıdır, veri yolu farkı değil — ve burada zaten bir kez yanlış yayımlanmış rakama yol açtı. Aşağıdaki her laboratuvar rakamı, her iki Rust çekirdeğini de tek iş parçacığına sabitler. Bu tablodaki Go ve çekirdek uygulamaları kendi varsayılanlarında çalıştı; bunlar birden fazla iş parçacığı kullanır — bu da bir şey değiştirirse onları tek iş parçacığımıza karşı avantajlı gösterir. Teslim edildiği haliyle varsayılan fark, göz ardı edilerek gizlenmesin diye ayrıca, bir kez gösterilir.
Neyin, ne üzerinde ölçüldüğü
İki bağımsız ortam, çünkü bir kıyaslama yalnızca çalıştığı ortam içinde dürüsttür — bir iperf3
rakamı ile bir bandwidth-test rakamı, iki isim taşıyan tek bir rakam değildir ve bir dizüstü
bilgisayarın çekirdeklerini paylaşan bir VM bir yönlendirici değildir.
Donanım — iki MikroTik yönlendirici, bir gigabit LAN, 2026-09-01. Gönderici olarak
RB3011UiAS (ARMv7, 2×1400 MHz), alıcı olarak cAP ax (ARM64, 4×864 MHz), ikisinde de RouterOS
7.24.1. Çekirdeğimiz, bir RouterOS konteynerinde, kullanıcı alanında, fv olarak, yönlendiricinin
kendi çekirdek içi WireGuard protokolü uygulamasına karşı çalıştı — bu donanımda mevcut olan tek
elma-elma karşılaştırması, çünkü RouterOS'un kendisinde sunulan tek alternatif budur. İki üretici,
çünkü hiçbiri uygulamaları temiz biçimde geçmez: konteyner çalıştırmamız için dd | nc, çekirdek
çalıştırması için RouterOS'un kendi bandwidth-test'i — her biri kontrol olarak kendi tünelsiz
tavanını taşır, böylece okuyucu orana güvenmeden önce tavanı akıl kontrolünden geçirebilir.
Laboratuvar — bir Apple silikon anamakine üzerinde QEMU/HVF altında dört aarch64 Debian konuğu,
2026-08-31. Test edilen her ikili arasında aynı anahtar çiftleri, adresler, portlar, MTU ve
arayüz açılışı: çekirdeğimiz, çekirdeğimizin çatallandığı kullanıcı alanı çekirdeği olan
boringtun 0.7, wireguard-go (WireGuard protokolünün referans uygulaması, kullanıcı alanı) ve
Linux çekirdeğinin kendi WireGuard protokolü modülü. 15 saniyelik iperf3 çalıştırmaları, her
birinden üç örnek, yukarıdaki uyarı kutusundaki nedenden dolayı her iki Rust çekirdeği de tek bir
veri düzlemi iş parçacığına sabitlendi.
task test:impls # lab: ours vs boringtun vs wireguard-go vs the kernel
task test:all # lab: single-hop, multi-hop, and the comparisonYönlendirici çalıştırması henüz bir task hedefi değildir — her kutuda RouterOS'un yerleşik
WireGuard protokolü sunucusuna karşı, bir RouterOS konteynerinde elle başlatılan fv'dir. Tam
topoloji ve her ham satır kendi docs/benchmarks/results.csv dosyamızda yer alır.
Önemli olan sayı: yönlendiricide
| Ölçüt | çekirdeğimiz (kullanıcı alanı) | yönlendiricinin kendi çekirdek tüneli |
|---|---|---|
| aktarım hızı | 122 Mbit/s | 66.8 Mbit/s |
| CPU, gönderici | 65% (63–67) | 74% (70–79) |
| CPU, alıcı | 18% (10–22) | 20% (18–21) |
| Mbit başına CPU | 0.53 %/Mbit | 1.10 %/Mbit |
| RTT | 1.22 ms | 0.92 ms |
| yerleşik bellek (gönderici / alıcı) | 3.0 / 3.1 MB | çekirdek içi, ayrıştırılamaz |
| üretici | dd | nc | bandwidth-test |
| o üreticinin tünelsiz tavanı | 187 Mbit/s | 220.5 Mbit/s |
| kendi tavanına karşı korunan | 65% | 30% |
Çubuk uzunluğu ∝ Mbit/s, eksen 0–140. Her iki çalıştırmada da aynı RB3011 göndericisi; baştan sona CPU tavanı odur, alıcı cAP ax neredeyse %20'de boşta beklerken %65–79 arasında.
İki bağımsız çerçeveleme birbirini doğruluyor; bu da sonucu seçilerek değil inandırıcı kılan şeydir: mutlak aktarım hızı çekirdeğimizi 1.8× lehine çeviriyor, her üreticinin kendi tartışmasız tavanına karşı korunan aktarım hızı ise onu 2.2× lehine çeviriyor. Tablonun söylemediği şey: iki üretici aynı araç değildir, bu yüzden tek bir manşet oranı yerine her iki tünelsiz tavan da yazdırılmıştır ve gönderici trafiği yerel olarak üretmek yerine gerçek ağ trafiği alsaydı her iki rakam da değişirdi. Aşağıdaki laboratuvar tablosunun aksine, bunlar tekil çalıştırmalardır: köşeli parantez içindeki yüzdeler, o çalıştırma sırasında CPU'nun dolaştığı aralıktır, tekrarlanan denemeler arasındaki bir yayılma değildir.
Laboratuvar: üç kullanıcı alanı çekirdeği, bir çekirdek, eşitlenmiş iş parçacıkları
| çekirdek | tür | TCP Mbit/s (aralık, n=3) | zirve bellek | p99 gecikme |
|---|---|---|---|---|
| bizimki, 1 iş parçacığı | kullanıcı alanı, Rust | 1535 (1506–1554) | 3.5 MB | 0.94 ms |
| boringtun, 1 iş parçacığı | kullanıcı alanı, Rust | 1427 (1346–1479) | 5.3 MB | 1.15 ms |
| wireguard-go, varsayılan | kullanıcı alanı, Go | 1228 (1221–1239) | 34.8 MB | 1.39 ms |
| çekirdek WireGuard, varsayılan | çekirdek, C | 1376 (1366–1386) | — (çekirdek, ayrıştırılamaz) | 0.96 ms |
Teslim edildiği haliyle, eşitlenmemiş: çekirdeğimiz varsayılan olarak 1 iş parçacığı kullanır ve 1535 Mbit/s çalıştı; boringtun varsayılan olarak 4 iş parçacığı kullanır ve bu ayarda ~789 Mbit/s çalıştı. Yalnızca varsayılana karşı varsayılan rakamını bildirmek, aynı hatayı göz alıcı yönde tekrarlardı. Her iki rakam da gerçektir; yalnızca biri veri yolunu iş parçacığı sayısından ayırır.
CPU maliyeti, laboratuvarın bu yazıdaki yerini hak ettiği noktadır — bu, yönlendirici tablosunun gösteremediği tek eksendir, çünkü bir yönlendiricinin çekirdek içi karşılaştırma öğesi, karşılaştıracak bir CPU dökümü vermez.
Düşük olması daha iyidir. Eksen 0–2.5 s/Gbit, doğrusal. Her iki bölüm de soldan sağa yığılır; bölünme, bir sonraki bölümün kaybının göründüğü yerdir.
Logaritmik ölçek, 1–50 MB, belirtiliyor çünkü dürüst varsayılan bu değildir: doğrusal bir eksende wireguard-go'nun 34.8 MB'ı diğer iki çubuğu ince bir çizgiye düzleştirirdi.
Kazanmadığımız yerler
İki yer, ikisi de CSV'nin içine gömülmek yerine açıklanıyor:
Laboratuvarda kullanıcı modu CPU'su. Daha ucuz Rust karşılaştırma öğesinin 0.290'ına karşı 0.347 s/Gbit — o tabloda önümüzde olduğu tek sütun. Bizim okumamız, bunun israf değil satın alınmış olduğudur: çekirdeğimiz her iki yönde de TUN sağlama toplamı boşaltması (checksum offload) yapar; bu da işi çekirdekten alıp kullanıcı zamanı olarak sayılan kullanıcı alanı CPU'suna taşır — ve bu değiş tokuşun geri ödendiği sistem-CPU sütunu rahatça önde (0.549'a karşı 0.787). Toplam CPU yine de kazanıyor. Yukarıdaki kullanıcı/sistem bölünmesi, özellikle bu değiş tokuşun ileri sürülmek yerine görünür olması için gösteriliyor.
Yönlendiricide gidiş-dönüş süresi. Çekirdek tünelinin 0.92 ms'ine karşı 1.22 ms — çekirdek içi çözümün açıkça kazandığı tek donanım ekseni; bunun sıradan nedeni, bizim yolumuzun bir konteynerin veth sıçramasına ve çekirdek içi bir yolun ödemediği bir kullanıcı alanı zamanlama dönüşüne mal olmasıdır. İlginç biçimde laboratuvar, p99 gecikmesinde tersi bir sıralama gösteriyor (çekirdeğin 0.96 ms'ine karşı 0.94 ms) — bunun ötesinde “farklı ortamlar, farklı darboğazlar” dışında bir anlam çıkarmıyoruz ve hangi rakam daha göz alıcıysa onu seçmek yerine bunu söylüyoruz.
Neyin taze, neyin devralınmış olduğu
Yukarıdaki her rakam bir tarih taşır ve tarihler önemlidir: yönlendirici tablosu 2026-09-01,
laboratuvar tablosu 2026-08-31'dir. O zamandan beri, koordineli bir major-sürüm sıçraması, veri
düzlemimizin her pakette çağırdığı temel kripto crate'lerini taşıdı — chacha20poly1305,
blake2, curve25519-dalek — ve o ağaca karşı tam laboratuvar veya yönlendirici karşılaştırmasını
yeniden çalıştırmadık. Yukarıdaki rakamlar gerçektir ve dikkatle ölçülmüştür, ama yayımlandığı
tarih itibarıyla altı gün eskidir, bu sıçramadan önceki bir ağaç üzerindedir. Bunları sessizce
değil, bunu belirterek yayımlıyoruz ve elimize geçtiğinde bir yeniden çalıştırmayı tarihleyeceğiz.
Yönlendirici rakamı da kendi kayıtlarımızda belirli bir commit'e sabitlenmiş değildir —
“2026-09-01”i bir optimizasyon değişikliğine bağlı olarak değil, “eylül başı” olarak okuyun.
Bugün, bu makinede, bir yönlendirici veya VM laboratuvarı olmadan yeniden çalıştırabileceğimiz şey, kripto sıçramasının en doğrudan dokunduğu yoldaki kısımdır: yukarıdaki her soket, TUN ve iş parçacığı kaygısından yalıtılmış encapsulate ve decapsulate maliyeti.
| işlem, paket boyutu | çekirdeğimiz | boringtun 0.7.1 |
|---|---|---|
| kapsülleme, 64 B | 219.2 ns | 218.7 ns |
| kapsülleme, 512 B | 436.4 ns | 442.5 ns |
| kapsülleme, 1420 B | 938.1 ns | 968.3 ns |
| kapsül açma, 64 B | 238.8 ns | 226.7 ns |
| kapsül açma, 512 B | 562.1 ns | 539.2 ns |
| kapsül açma, 1420 B | 1099.5 ns | 1069.4 ns |
2026-09-05'te, Apple M3 Max üzerinde,
cargo bench -p forestvpn-quantum --bench datapath_benches -- --measurement-time 3 --warm-up-time 1
ile, criterion 0.8.2, nokta başına 100 örnekle yeniden çalıştırıldı. Soket yok, TUN yok, iş
parçacığı yok — tamamlanmış tek bir el sıkışma, ardından döngü içinde kapsülleme/kapsül açma.
Sonuçlar yöne ve paket boyutuna göre karışıktır, her iki yönde de birkaç yüzde içindedir; tam
olarak beklediğimiz şey de budur: çekirdeğimiz bu yolu üst akışından çatalladı ve ikisi de artık
aynı RustCrypto ilkel nesneleri kuşağı üzerinde duruyor. Bu bir aktarım hızı iddiası değildir —
çatallanmış kripto yolunun kendisinin, gürültü payı içinde, üst akışınkiyle yaklaşık aynı maliyete
sahip olduğunu söyler; bu da yukarıdaki laboratuvar ve yönlendirici kazanımlarını güvenilir kılan
şeydir: bunlar yığının başka bir yerinden gelir (syscall toplu işleme, boşaltma, iş parçacığı),
daha hızlı bir şifreleyiciden değil.
Yeniden üretme
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 1Yönlendirici çalıştırması bugün elle yapılır: her kutuda bir RouterOS konteynerinde fv,
RouterOS'un kendi WireGuard protokolü sunucusuna karşı.
Bunun size ne kazandırdığı konusunda dürüst olmak gerekirse: düzenek ve buradaki her tablonun
arkasındaki ham results.csv, bugün herkese açık olmayan kendi ağacımızda yaşıyor. Yukarıdaki
komutları, klonlayıp tekrarlayabileceğiniz bir şey olarak değil, çalıştırdığımızın tam bir ifadesi
olarak okuyun. Bize en az bağımlı olan iddia, kontrol etmeye en çok değer olandır da —
boringtun ve wireguard-go herkese açıktır, iperf3 herkese açıktır ve bu yazının başındaki iş
parçacığı sayısı tuzağı (tek bir bayrağın bir karşılaştırma öğesini tek bir makinede kabaca 789 ile
1427 Mbit/s arasında oynatması) bu üçüne sahip ve döngüde bizim kodumuz olmayan herkes tarafından
yeniden üretilebilir. Sizde de doğrulanırsa, tartışılacak satırlar yukarıdaki eşitlenmiş
satırlardır. Doğrulanmazsa, bunu duymak isteriz.