TCP, HTTP und der Multi-Threading Sweet Spot

Ich versuche zu verstehen, welche Leistungszahlen ich erhalte und wie ich die optimale Anzahl von Threads bestimme.

Unten in diesem Beitrag finden Sie meine Ergebnisse

Ich habe einen experimentellen Web-Client mit mehreren Threads in Perl geschrieben, der eine Seite herunterlädt, die Quelle für jedes Bild-Tag ermittelt und das Bild herunterlädt - wobei die Daten verworfen werden.

Es wird eine nicht blockierende Verbindung mit einer anfänglichen Zeitüberschreitung pro Datei von 10 Sekunden verwendet, die sich nach jeder Zeitüberschreitung und jedem erneuten Versuch verdoppelt. Außerdem werden IP-Adressen zwischengespeichert, sodass jeder Thread nur einmal nach DNS suchen muss.

Die Gesamtmenge der heruntergeladenen Daten beträgt 2271122 Bytes in 1316 Dateien über eine 2,5Mbit-Verbindung vonhttp://hubblesite.org/gallery/album/entire/npp/all/hires/true/ . Die Miniaturbilder werden von einem Unternehmen gehostet, das sich nach eigenen Angaben auf niedrige Latenzzeiten für Anwendungen mit hoher Bandbreite spezialisiert hat.

Wandzeiten sind:

1 Thread benötigt 4:48 - 0 Timeouts
2 Threads benötigen 2:38 - 0 Timeouts
5 Threads dauern zwischen 2:22 und 20 Timeouts
10 Threads dauern zwischen 2:27 und 40 Timeouts
50 Threads benötigen zwischen 2:27 und 170 Timeouts

Im schlimmsten Fall (50 Threads) verbraucht der Client weniger als 2 Sekunden CPU-Zeit.

Durchschnittliche Dateigröße 1,7 KB
Durchschnitt rtt 100 ms (gemessen durch Ping)
durchschn. cli cpu / img 1 ms

Die schnellste durchschnittliche Download-Geschwindigkeit beträgt 5 Threads mit insgesamt etwa 15 KB / s.

Der Server scheint tatsächlich eine recht niedrige Latenz zu haben, da es nur 218 ms dauert, um jedes Image abzurufen, was bedeutet, dass der Server im Durchschnitt nur 18 ms benötigt, um jede Anforderung zu verarbeiten:

0 cli sendet syn
50 srv rcvs syn
50 srv sendet syn + ack
100 cli conn Established / cli Sends get
150 srv recv's bekommen
168 srv liest Datei, sendet Daten, ruft ab
218 cli recv HTTP-Header + vollständige Datei in 2 Segmenten MSS == 1448

Ich kann feststellen, dass die durchschnittliche Download-Geschwindigkeit pro Datei aufgrund der geringen Dateigröße und der relativ hohen Kosten pro Datei für den Verbindungsaufbau niedrig ist.

Was ich nicht verstehe, ist, warum ich praktisch keine Verbesserung der Leistung über 2 Threads hinaus sehe. Der Server scheint ausreichend schnell zu sein, aber das Zeitlimit für Verbindungen wird bereits bei 5 Threads festgelegt.

Die Zeitüberschreitungen scheinen nach ungefähr 900 - 1000 erfolgreichen Verbindungen zu beginnen, unabhängig davon, ob es sich um 5 oder 50 Threads handelt. Ich gehe davon aus, dass dies wahrscheinlich eine Art Drosselungsschwelle auf dem Server ist, aber ich würde erwarten, dass 10 Threads immer noch deutlich schneller als 2 sind.

Vermisse ich hier etwas?

EDIT-1

Nur zu Vergleichszwecken habe ich die DownThemAll Firefox-Erweiterung installiert und die Bilder damit heruntergeladen. Ich habe es auf 4 gleichzeitige Verbindungen mit einem Timeout von 10 Sekunden eingestellt. Der DTM brauchte ungefähr 3 Minuten, um alle Dateien herunterzuladen und auf die Festplatte zu schreiben. Nach ungefähr 900 Verbindungen traten Zeitüberschreitungen auf.

Ich werde tcpdump ausführen, um ein besseres Bild davon zu bekommen, was auf der TCP-Protokollebene vor sich geht.

Ich habe auch den Firefox-Cache geleert und auf "Neu laden" geklickt. 40 Sekunden, um die Seite und alle Bilder neu zu laden. Das schien viel zu schnell - vielleicht hat Firefox sie in einem nicht gelöschten Speicher-Cache aufbewahrt? Also habe ich Opera geöffnet und es hat auch ungefähr 40 Sekunden gedauert. Ich nehme an, sie sind so viel schneller, weil sie HTTP / 1.1-Pipelining verwenden müssen.

Und die Antwort ist! ??

Nachdem ich ein wenig mehr Code getestet und geschrieben hatte, um die Sockets per Pipelining wiederzuverwenden, fand ich einige interessante Informationen.

Bei Ausführung mit 5 Threads ruft die Nicht-Pipeline-Version die ersten 1026 Bilder in 77 Sekunden ab, benötigt jedoch weitere 65 Sekunden, um die verbleibenden 290 Bilder abzurufen. Dies bestätigt ziemlich genauMattH's Theorie darüber, dass mein Klient von a getroffen wirdSYN FLOOD Ereignis, das dazu führt, dass der Server für einen kurzen Zeitraum nicht mehr auf meine Verbindungsversuche reagiert. Dies ist jedoch nur ein Teil des Problems, da 77 Sekunden für 5 Threads immer noch sehr langsam sind, um 1026 Bilder zu erhalten. wenn du das entfernstSYN FLOOD Problem, es würde immer noch etwa 99 Sekunden dauern, um alle Dateien abzurufen. Also basierend auf ein wenig Recherche und einigemtcpdumpAnscheinend ist der andere Teil des Problems die Latenz und der Verbindungsaufbau-Overhead.

Hier kommen wir zurück zum Thema "Sweet Spot" oder der optimalen Anzahl von Threads. Ich habe den Client geändert, um HTTP / 1.1-Pipelining zu implementieren, und festgestellt, dass die optimale Anzahl von Threads in diesem Fall zwischen 15 und 20 liegt. Beispiel:

1 Thread benötigt 2:37 - 0 Timeouts
2 Threads benötigen eine Zeitüberschreitung von 1:22 - 0
5 Threads dauern 0:34 - 0 Zeitüberschreitungen
10 Threads benötigen eine Zeitüberschreitung von 0:20 - 0
11 Threads benötigen eine Zeitüberschreitung von 0:19 - 0
15 Threads benötigen eine Zeitüberschreitung von 0:16 - 0

Es gibt vier Faktoren, die dies beeinflussen; Latenz / RTT, maximale End-to-End-Bandbreite, Empfangspuffergröße und Größe der heruntergeladenen Bilddateien.Auf dieser Website erfahren Sie, wie sich die Größe des Empfangspuffers und die RTT-Latenz auf die verfügbare Bandbreite auswirken.

Darüber hinaus wirkt sich die durchschnittliche Dateigröße auf die maximale Übertragungsrate pro Verbindung aus. Jedes Mal, wenn Sie eine GET-Anforderung ausgeben, erstellen Sie eine leere Lücke in Ihrer Übertragungsleitung, die der Größe der RTT-Verbindung entspricht. Wenn Sie beispielsweise eine maximal mögliche Übertragungsrate (empfangene Buff-Größe / RTT) von 2,5 MBit und eine RTT von 100 ms haben, führt jede GET-Anforderung zu einer Lücke von mindestens 32 KB in Ihrer Pipe. Bei einer großen durchschnittlichen Bildgröße von 320 KB entspricht dies einem Overhead von 10% pro Datei, wodurch die verfügbare Bandbreite effektiv auf 2,25 MBit reduziert wird. Bei einer kleinen durchschnittlichen Dateigröße von 3,2 kB springt der Overhead jedoch auf 1000% und die verfügbare Bandbreite wird auf 232 kBit / s reduziert - ungefähr 29 kB.

So finden Sie die optimale Anzahl von Threads:

Spaltgröße = MPTR * RTT
MPTR / (MPTR / Lückengröße + AVG-Dateigröße) * AVG-Dateigröße)

Für mein obiges Szenario ergibt dies eine optimale Threadanzahl von 11 Threads, was meinen tatsächlichen Ergebnissen sehr nahe kommt.

Wenn die tatsächliche Verbindungsgeschwindigkeit langsamer als die theoretische MPTR ist, sollte sie stattdessen für die Berechnung verwendet werden.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage