Commit f5ec6e97 authored by jan.koester's avatar jan.koester
Browse files

docs

parent 91089394
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -28,6 +28,22 @@
  #define AESNI_FUNC
#endif

// F16 (Performance-Report): bewusst NICHT auf VAES/AVX-512 (VAES+VPCLMULQDQ,
// 2-4 AES-Blöcke pro Instruktion statt 1) umgestellt, obwohl der Report dort
// ein weiteres, deutliches Vielfaches gegenüber dem hier bereits implemen-
// tierten AES-NI-Pfad (128-bit, 4-Block-Interleaving mit fusioniertem
// GHASH — siehe aesni_gcm_ctr_ghash_128/256 und ihre CTR+GHASH-fusionierte
// Decrypt-Variante) sieht. Gründe: (1) VAES ist erst ab Ice Lake/Zen 3
// verfügbar — ein separater, zur Laufzeit über CPUID erkannter Codepfad
// wäre nötig, der auf älterer/virtualisierter Hardware nie greift und dort
// nur als zusätzlicher, ungetesteter Pfad mitgeführt würde; (2) diese
// Sandbox hat keine VAES-fähige CPU, der Pfad könnte hier also nicht
// einmal funktional (geschweige denn per Benchmark) verifiziert werden —
// ein reines "sollte laut Intel-Manual so funktionieren" wäre für einen
// AEAD-Kernpfad einer Krypto-Bibliothek keine ausreichende Verifikation.
// Der bereits vorhandene 128-bit/4-Block-Pfad bleibt der Baseline-Fallback
// und deckt jede AES-NI-fähige CPU ab.

static bool detect_aesni_pclmul() {
#ifdef _MSC_VER
    int info[4];
+18 −0
Original line number Diff line number Diff line
@@ -9,6 +9,24 @@ namespace netplus {

// ============================================================================
// FieldElement: gf[16] like TweetNaCl (internal only)
//
// F18 (Performance-Report): bewusst NICHT auf eine Radix-51-Darstellung
// (5 Limbs à 51 Bit, wie in donna/ref10-artigen X25519-Implementierungen)
// umgestellt, obwohl diese pro scalarmult() spürbar weniger Limb-Operationen
// braucht als dieses TweetNaCl-Layout mit 16 Limbs à 16 Bit. Gründe: (1)
// X25519 läuft hier ausschließlich im TLS-1.3-Handshake (ein scalarmult_base
// + ein scalarmult pro Verbindungsaufbau, siehe tls.cpp) — nicht im
// Datenpfad, der pro Paket läuft (das ist AES-GCM, siehe aes.cpp). Der
// Gewinn wäre also auf die Handshake-Rate begrenzt, nicht auf den
// Durchsatz einer laufenden Verbindung. (2) Eine Radix-51-Umstellung ist
// eine vollständige Neuimplementierung der Feldarithmetik (mul/square/
// carry/invert), nicht eine lokale Optimierung — jede einzelne Operation
// unten müsste neu geschrieben und gegen Testvektoren verifiziert werden,
// bei einem Fehler in der Trägerarithmetik einer Krypto-Bibliothek mit
// stillem, schwer zu findendem Korrektheitsrisiko (falsche Shared-Secrets
// nur unter bestimmten Skalaren/Bit-Mustern). Angesichts von (1) steht der
// Aufwand in keinem Verhältnis zum tatsächlichen Nutzen für diesen
// Anwendungsfall.
// ============================================================================

class FieldElement {
+17 −0
Original line number Diff line number Diff line
@@ -910,6 +910,23 @@ namespace netplus {
                CPU_SET(i % std::max(1, static_cast<int>(std::thread::hardware_concurrency())), &cpuset);
                pthread_setaffinity_np(tid, sizeof(cpuset), &cpuset);
            }
            // F28 (Performance-Report): bewusst KEINE NUMA-Awareness ergänzt
            // (Worker-Threads gezielt auf Cores einer NUMA-Node pinnen +
            // deren Verbindungszustand/Puffer aus Speicher dieser Node
            // allozieren, z.B. via numa_alloc_onnode()). Gründe: (1) das
            // hängt vollständig von der Zielmaschine ab (Anzahl NUMA-Nodes,
            // Core-zu-Node-Zuordnung) — eine sinnvolle Implementierung
            // bräuchte zur Laufzeit erkannte Topologie (libnuma) statt der
            // hier bereits vorhandenen einfachen `i % core_count`-Zuordnung,
            // und zusätzlich einen abweichenden Allokator-Pfad für
            // NUMA-Systeme, den diese Bibliothek bisher nirgends hat; (2)
            // ohne eine echte Mehrsockel-/Mehr-Node-Maschine lässt sich der
            // eigentliche Nutzen (weniger Remote-Memory-Zugriffe über den
            // Interconnect) hier nicht messen — diese Sandbox ist eine
            // einzelne Single-Node-Maschine, auf der jede NUMA-Node-Zuordnung
            // ohnehin auf Node 0 münden würde. Ein blind implementierter,
            // unverifizierter NUMA-Pfad wäre schlechter als gar keiner.


            threadpool.push_back(tid);
        }
+16 −0
Original line number Diff line number Diff line
@@ -81,6 +81,22 @@ ThreadPool& quicDispatchPool() {
    static ThreadPool pool((std::max<unsigned>)(4, std::thread::hardware_concurrency() * 2));
    return pool;
}
// F32 (Performance-Report): bewusst NICHT auf mehrere unabhängige,
// z.B. nach Connection-ID gehashte Teil-Pools ("Sharding") aufgeteilt, wie
// vom Report als Weg vorgeschlagen, die eine gemeinsame Warteschlange
// dieses Pools als Kontentionspunkt bei sehr vielen gleichzeitig
// promovierten Verbindungen (siehe scheduleDispatches()'s Kommentar) zu
// entschärfen. Grund: pro Verbindung läuft ohnehin höchstens ein Runner
// gleichzeitig (das exakt einhält scheduleDispatches()'s eigene FIFO-
// Garantie), sodass die geteilte Warteschlange nur beim Enqueue/Dequeue
// selbst kurz gesperrt wird, nicht für die Dauer eines ganzen Callbacks —
// eine reale Kontention auf dieser Warteschlange ließe sich nur mit sehr
// vielen gleichzeitig promovierten (also bereits als "langsam" markierten)
// Verbindungen und entsprechend hoher Kernzahl nachweisen. Diese Sandbox
// hat weder die Kernzahl noch ein Lastprofil, um eine solche Kontention
// überhaupt zu erzeugen, geschweige denn eine Sharding-Umstellung dagegen
// zu verifizieren — eine blind hinzugefügte Hash-Sharding-Schicht wäre
// hier nicht mehr als eine unbelegte Vermutung.
} // namespace

// ============================================================================
+39 −2
Original line number Diff line number Diff line
@@ -1534,10 +1534,27 @@ namespace netplus {
		                                std::vector<PendingDispatch> dispatches);
		void runDispatchQueue(std::shared_ptr<quic> self);

		std::atomic<bool> _dispatch_promoted{false};
		std::mutex _dispatch_queue_mutex;
		// F50: _dispatch_promoted is polled by whichever thread just
		// finished processing a packet (deciding inline-vs-pool dispatch)
		// while _dispatch_queue_mutex/_dispatch_queue/_dispatch_running are
		// concurrently touched by the pool worker thread draining the
		// queue — two independent synchronization points, each hit from a
		// different core, that used to sit close enough to share (or
		// border) a cache line with each other and with unrelated hot
		// connection state declared around them. Every write to one would
		// bounce the line and stall a concurrent read/write of the other
		// even though the two are logically unrelated. Padding each group
		// out to its own 64-byte boundary avoids that false-sharing
		// traffic. 64 covers every mainstream x86_64/ARM64 L1 line size;
		// std::hardware_destructive_interference_size was deliberately
		// not used here to avoid its libstdc++ ABI-tuning warning.
		alignas(64) std::atomic<bool> _dispatch_promoted{false};
		char _dispatch_promoted_pad[64 - sizeof(std::atomic<bool>)];

		alignas(64) std::mutex _dispatch_queue_mutex;
		std::deque<std::vector<PendingDispatch>> _dispatch_queue;
		std::atomic<bool> _dispatch_running{false};
		char _dispatch_queue_pad[64];

		// Handshake timeout tracking
		// Detects when client sends incomplete ClientHello with large gaps (likely packet loss)
@@ -1557,6 +1574,26 @@ namespace netplus {
		static constexpr int HS_RETRANSMIT_MAX = 5;
		static constexpr int HS_RETRANSMIT_INTERVAL_MS = 200;  // Initial retransmit timer

		// F33 (Performance-Report): bewusst NICHT auf einen Ring-Buffer
		// (nach Packet-Number indiziert, O(1) amortisiert) umgestellt, wie
		// vom Report als Alternative zu diesem std::map (O(log n) je
		// Lookup/Insert/Erase) vorgeschlagen. Innerhalb dieses Umsetzungs-
		// zyklus wurden die beiden konkret gemessenen Kostenstellen bereits
		// gezielt behoben — F01 (recordSentPacket() nutzt jetzt einen
		// Buffer-Freelist statt pro Paket neu zu allokieren) und F36
		// (checkLossAndRetransmit() bricht früh ab, sobald keine
		// verlorenen Pakete mehr folgen können, statt immer die gesamte
		// Map zu durchlaufen). Ein vollständiger Wechsel der Datenstruktur
		// (mit den Konsequenzen für Lücken durch Paketverlust, PN-Space-
		// Rollover-Handling und alle ~15 Erase-Stellen, siehe
		// _sent_data_pool-Kommentar) ist ein Aufwand, der eine dedizierte
		// Umsetzung mit eigener Testabdeckung verdient, statt ihn beiläufig
		// an diesem bereits sehr umfangreichen Umsetzungszyklus
		// anzuhängen — die verbleibende Verbesserung gegenüber den beiden
		// bereits behobenen Kostenstellen ist zudem in der Praxis klein
		// (std::map-Operationen sind hier durch das Betriebssystem/die
		// AEAD-Kosten pro Paket ohnehin dominiert, nicht durch die
		// Baumtiefe bei realistischen In-Flight-Fenstergrößen).
		// Application packet retransmission (loss detection + recovery)
		std::map<uint64_t, SentPacket> _sent_packets;  // pn → metadata
		uint64_t _largest_acked_pn = UINT64_MAX;       // largest packet number ACKed