Mehrere TCP/IP Verbindungen
-
Hast du irgendwelche Papers/Blog-Artikel/... dazu, wo jemand wirklich einen (fairen, sinnvollen) Vergleich gemacht hat?
Mir fällt nämlich wirklich guter Grund ein warum es spürbar langsamer sein sollte. Vielleicht im einstelligen Prozentbereich, aber den kann man denke ich auch bei ein paar tausend gleichzeitigen Verbindungen vernachlässigen. Wenn man mit einrechnet um wie viel einfacher alles bei "1 thread pro connection" wird.
Das ist einfach so ne Sache die man oft im Netz liest, aber ich persönlich hatte noch nie ein "shit, zu viele Threads" Problem (wobei meine Server auch üblicherweise net mehr als ein paar wenige huntert gleichzeitige Connections haben). Und wie gesagt, ich kann mir auch net vorstellen was hier signifikant bremsen sollte.
64 Bit OS und Prozess mal vorausgesetzt.ps: Ich meine damit nur den Netzwerk-IO. File-IO oder DB-Zugriffe sind ne andere Geschichte. Wenn man die "implizit" durch ne limitierte Anzahl an Worker-Threads in der asynchronen Variante limitiert, dann muss man die natürlich auch explizit in der gethreadeten Variante limitieren. Über Semaphoren oder was auch immer. Sonst ist es eben mMn. kein fairer Vergleich des Netzwer-IO Demultiplexing Mechanismus' mehr.
-
hustbaer schrieb:
Mir fällt nämlich wirklich guter Grund ein warum es spürbar langsamer sein sollte. Vielleicht im einstelligen Prozentbereich, aber den kann man denke ich auch bei ein paar tausend gleichzeitigen Verbindungen vernachlässigen. Wenn man mit einrechnet um wie viel einfacher alles bei "1 thread pro connection" wird.
Threads sind ein sehr allgemeines Mittel, das alles kann, aber nichts optimal. Das sind Fibers, die nur über teure Systemaufrufe angehalten und gestartet werden können und untrennbar mit dem zentralen Scheduler des Kernels verheiratet sind.
Einen Thread mit
readoderwriteschlafen zu legen, ist nicht kostenlos. Da müssen Locks im Scheduler genommen werden. Wenn etwas fertig wird, muss wieder der Scheduler angefasst werden und wenn der mal Zeit frei hat, wird der Thread fortgesetzt, was auch nicht kostenlos ist.Deswegen hat man
epollgeschaffen, mit dem sich viele Operationen mit einem einzigen Systemaufruf schlafen legen können. Wenn mehrere Operationen zur ungefähr gleichen Zeit fertig werden, muss derepoll-Thread nur einmal geweckt werden und arbeitet dann alle Ergebnisse ab. Wenn nichts mehr zu tun ist, legt der sich erst wieder schlafen.Es wäre vielleicht sinnvoller, wenn der Kernel dem Userland mehr Kontrolle über den Scheduler geben würde. Threads würde man dann bedarfsgerecht im Userland implementieren.
Noch sinnvoller wäre es, wenn wir mit der Unterscheidung zwischen Kernel und Userland aufhören würden. Dieser rostige Maschendrahtzaun zwischen Kernel und Userland heißt Unix. Um mit Unix effizient zu arbeiten, braucht man C(++). Um C(++) zu bändigen, braucht man Unix-Konzepte. Vielleicht kann Rust oder dessen Nachfolger diesen Irrsinnskreis irgendwann brechen.
-
TyRoXx schrieb:
hustbaer schrieb:
Mir fällt nämlich wirklich guter Grund ein warum es spürbar langsamer sein sollte. Vielleicht im einstelligen Prozentbereich, aber den kann man denke ich auch bei ein paar tausend gleichzeitigen Verbindungen vernachlässigen. Wenn man mit einrechnet um wie viel einfacher alles bei "1 thread pro connection" wird.
Threads sind ein sehr allgemeines Mittel, das alles kann, aber nichts optimal. Das sind Fibers, die nur über teure Systemaufrufe angehalten und gestartet werden können und untrennbar mit dem zentralen Scheduler des Kernels verheiratet sind.
Einen Thread mit
readoderwriteschlafen zu legen, ist nicht kostenlos. Da müssen Locks im Scheduler genommen werden. Wenn etwas fertig wird, muss wieder der Scheduler angefasst werden und wenn der mal Zeit frei hat, wird der Thread fortgesetzt, was auch nicht kostenlos ist.Deswegen hat man
epollgeschaffen, mit dem sich viele Operationen mit einem einzigen Systemaufruf schlafen legen können. Wenn mehrere Operationen zur ungefähr gleichen Zeit fertig werden, muss derepoll-Thread nur einmal geweckt werden und arbeitet dann alle Ergebnisse ab. Wenn nichts mehr zu tun ist, legt der sich erst wieder schlafen.Es wäre vielleicht sinnvoller, wenn der Kernel dem Userland mehr Kontrolle über den Scheduler geben würde. Threads würde man dann bedarfsgerecht im Userland implementieren.
Noch sinnvoller wäre es, wenn wir mit der Unterscheidung zwischen Kernel und Userland aufhören würden. Dieser rostige Maschendrahtzaun zwischen Kernel und Userland heißt Unix. Um mit Unix effizient zu arbeiten, braucht man C(++). Um C(++) zu bändigen, braucht man Unix-Konzepte. Vielleicht kann Rust oder dessen Nachfolger diesen Irrsinnskreis irgendwann brechen.
Ach nöö. Ein Tread pro Client und fertig ist die Laube.
Daß das so nicht mehr und nicht weniger locks braucht, als jede andere Lösung, ist doch seit Jahrzehnten bewiesen.
Allenfalls bei mehr als 10k Clients steigen manche Betriebssysteme aus dabei. Dann kann man sich was überlegen. Am besten, man frickelt dann was in assembler, statt auf die törichte Idee zu kommen, von vorne herein geplant zu haben, daß vorgelagerte Rechner je ca 10k Clients betreuen, die wir uns bei 10k Last wohl dann locker leisten können. Hardware statt Arbeitszeit, also sagen wir mal ein Office-PC für 250€ statt 10 Wochen Frickeln. Und die Kacke skaliert erstmal wie Sau (im Gegensatz zum Frickeln). Die nächsten 50 Vorgelagerten klappen noch leidlich. Kannste mit Frickeln nen Faktor 50 rausholen? Und per-client-Leistung kann raufgezogen werden bis zum Anschlag!!! Also mal locker für Surplus-Premium-Kunden pro Client 1G history-Daten zum Besser-Komprimieren lagern, auf history verweisen statt nochmal zu senden, und dann natürlich noch megazip mit compression level 9 drauf.Damit meine ich nicht, daß man irgendwo im Entwicklungsprozess die Performance außer Acht lassen sollte. Ihr kennt mich, ich bin Speed-Freak. Aber bei den Sockets widerspreche ich, da geistern vorwiegend Meinungen rum, die sich nur einigermaßen wackelfrei auf Tatsachen stützen lassen, wenn man 20 bis 25 Jahre in der Zeit zurückreist.
Ein Thread pro Client ist echt eine Wonne. Endliche Automaten wie im RFC zu FTP? Klar, davon hab ich hunderte hinterm Haus auf dem Müll. Ich drehe den client-threads auf x86 auch mal den stack zu, sollen sie doch mit sagen wir mal 64k auskommen und heap gibts garnicht, wer braucht für stl nehme sich vom stack einen allocator zum bleistift. ach, erinnerungen, auf 64-bit isses ja soo egal. wieder ohne performanceverlust. nur mit adressraum (reichlich vorhanden) wird rumgesaut, die clients bleiben ebenso sparsam mit weniger programmieraufwand (evtl sogar ein wenig sparsamer, weils rumhudeln mit allocators nicht mehr ist).
Also wenn nicht gerade der Server so simple wie ein Zeitserver oder bofh-Server ist, würde ich stets einen Thread pro Client nehmen. Ich erkenne keine spürbaren Mehrkosten, sobald der Zweck des Servers mehr ist als nur bloß da zu sein, sondern mit den Daten auch noch was geschieht. Ich erkenne unermeßliche Vorteile bei einem Thread pro Client.
-
Ach nöö. Ein Tread pro Client und fertig ist die Laube.
ist eine einfache Lösung - ganz klar - und reicht sicherlich für seine Anforderungen
aber ab paar hundert(+) (denke so ab 300) Verbindungen mit Win7,x64,guter Prozessor wird das Umschalten zwischen den Threads schon mehr Zeit fressen als die TCP/IP-Verarbeitung an sich, da skaliert dann nichts mehr schön - erst wieder wenn man die Threads ueber verschiedene Prozesse(nicht Threads) verteilt, noch dazu ziehen die Threads auch ganz schoen Hauptspeicher weg (was auch bei Gigabytes RAM irgendwann bloed wird)
ansonsten werden die meisten Systeme die viele Verbindungen verwalten muessen mit
n Verbindungen pro Thread und n Prozessen geloest (z.B. Apache) - das "skaliert" dann auch wirklichAm besten, man frickelt dann was in assembler, statt auf die törichte Idee zu kommen, von vorne herein geplant zu haben
ich hoffe das war ein Witz - skalierbarkeit kann nur geplant funktionieren - die Zeit von Weg-"Optimierung" von Lastproblemen sind lange vorbeit, oder der Code ist wirklich totaler Müll (z.B. permanentes allokieren, kopieren, den Rest der schlimmen Dinge) - dann geht das schon noch

Skalierung bedeutet heute: wenig Threads mit (trivial) IO-Aufgaben, Multi-Threading und Multi-Prozessing, wenig Locking, sinnvolle Lockingstrategie, KEIN Assembler
-
Gast3 schrieb:
Ach nöö. Ein Tread pro Client und fertig ist die Laube.
ist eine einfache Lösung - ganz klar - und reicht sicherlich für seine Anforderungen
aber ab paar hundert(+) (denke so ab 300) Verbindungen mit Win7,x64,guter Prozessor wird das Umschalten zwischen den Threads schon mehr Zeit fressen als die TCP/IP-Verarbeitung an sich,
Ist mir recht egal, ob man Prozesse oder Threads nimmt. Warum dauert das Umschalten beo 300 Threads so lange?
ansonsten werden die meisten Systeme die viele Verbindungen verwalten muessen mit
n Verbindungen pro Thread und n Prozessen geloest (z.B. Apache) - das "skaliert" dann auch wirklichBeachte, daß http-serven trivial ist. Apache ist im Prinzip ein single-Threaded Nacheinanderausführer. Multi-Sachen nur, damit sich Clients nicht gegenseitig stören und um alle Prozessoren zu bewegen. Anfragen kann man prima lagern, bis Zeit da ist, dann komplett abarbeiten, und dann irgendwann raussenden, wenn wieder Zeit da ist. Allerdings werden logische Folgen wie if(einloggen());tun(); schon zerhackt, was man umständlich mit Session-Variablen und Wiedereinstiegspunkten simuliert.
Wenn man die Threadzahl einfach beschränken würde auf 75% Speicherauslastung, was hätte man dann für Mehrkosten? Pro Anfrage einmal Threadstart und -end, und kalte Sprungvorhersagetabellen? Macht großzügig aufgerundet hundert Mikrosekündchen? Aber die Webseite nudelt ein Script durch mod-reqrite, PHP und mySQK, und zahlt dafür 30ms.
-
Gast3 schrieb:
aber ab paar hundert(+) (denke so ab 300) Verbindungen mit Win7,x64,guter Prozessor wird das Umschalten zwischen den Threads schon mehr Zeit fressen als die TCP/IP-Verarbeitung an sich, da skaliert dann nichts mehr schön - erst wieder wenn man die Threads ueber verschiedene Prozesse(nicht Threads) verteilt, noch dazu ziehen die Threads auch ganz schoen Hauptspeicher weg (was auch bei Gigabytes RAM irgendwann bloed wird)
heisse luft
wo sind deine zahlen?
wo sind die sources mit denen man die benchmarks selbst wiederholen kann?
-
volkard schrieb:
Pro Anfrage einmal Threadstart und -end, und kalte Sprungvorhersagetabellen? Macht großzügig aufgerundet hundert Mikrosekündchen? Aber die Webseite nudelt ein Script durch mod-reqrite, PHP und mySQK, und zahlt dafür 30ms.
100 µs sind schon einmal die richtige Größenordnung. Invertiert sind das gerade einmal 10 kHz. Maximal gehen damit also 10000 Verbindungen pro Sekunde und Hardware-Thread. Das reicht sicher für 99% der Anwendungen. Wenn man aber über Performance spricht, meint man eigentlich immer die 1% der Anwendungen, bei denen tatsächlich optimiert werden muss. Bei den 1% lässt man sich dann etwas schlaueres einfallen als Threads.
Wenn man PHP benutzt, ist das sowieso alles egal. Dann kann man auch seinen Webserver in Bash mit
netcatschreiben.Beim Ausliefern von statischen Dateien wäre ein Thread pro Verbindung ein unnötiger Flaschenhals.
-
TyRoXx schrieb:
Beim Ausliefern von statischen Dateien wäre ein Thread pro Verbindung ein unnötiger Flaschenhals.
Ja.
TyRoXx schrieb:
Wenn man aber über Performance spricht, meint man eigentlich immer die 1% der Anwendungen, bei denen tatsächlich optimiert werden muss. Bei den 1% lässt man sich dann etwas schlaueres einfallen als Threads.
Akso sollten Leute, die noch fragen müssen, ob sie dies oder jenes machen, zu 99% die Antwort "Ein Thread pro Client" bekommen. Das sehe ich auch so.
Sowas da http://highscalability.com/blog/2013/5/13/the-secret-to-10-million-concurrent-connections-the-kernel-i.html betrifft weniger als 1% fürchte ich beinahe.
-
volkard schrieb:
Sowas da http://highscalability.com/blog/2013/5/13/the-secret-to-10-million-concurrent-connections-the-kernel-i.html betrifft weniger als 1% fürchte ich beinahe.
Und wenn es einen betrifft will man eh einen custom Treiber verwenden.

The kernel isn’t the solution. The kernel is the problem.
Besser hätte ich es nicht schreiben können.
-
Das eigentliche Hauptargument gegen Thread-per-Connection ist/war, dass es zwar skalierbar sein kann, es aber Systeme gibt/gab, wo der Scheduler eben nicht mit O(1) läuft, sodass diese Lösung nicht skalierbar UND plattformunabhängig war/ist.
Der Artikel ist ganz nett, sieht aber wie ein Horror-Szenario/Wunschdenken aus. Relativ betrachtet werden vielleicht 1% der Leute sich mit Millionen Verbindungen beschäftigen müssen, aber die absolute Zahl wird bestimmt ansteigen. Die sollen dann alle ihre eigenen Kernel-Mode-Treiber schreiben (á la Microsoft IIS' HTTP-Treiber) und lock-freie Algorithmen benutzen: allesamt extrem hoch spezialisierte Einzelbereiche. Kaum auszudenken, wie einem da die Bugs, ABA-Probleme, Deadlocks (und dank Treiber auch noch) Kernel-panics/Bluescreens um die Ohren fliegen werden.
-
TL;DR: Keiner von euch hat Erfahrung und ihr ratet alle rum.
-
mehr schrieb:
TL;DR: Keiner von euch hat Erfahrung und ihr ratet alle rum.
Machen das nicht alle Softwareentwickler die ganze Zeit? Erfahrung fällt nicht vom Himmel.
-
mehr schrieb:
TL;DR: Keiner von euch hat Erfahrung und ihr ratet alle rum.
Bei welchem Punkt?
Aber das alle rumraten, das glaube ich nicht.
-
Jodocus schrieb:
Die sollen dann alle ihre eigenen Kernel-Mode-Treiber schreiben (á la Microsoft IIS' HTTP-Treiber) und lock-freie Algorithmen benutzen: allesamt extrem hoch spezialisierte Einzelbereiche. Kaum auszudenken, wie einem da die Bugs, ABA-Probleme, Deadlocks (und dank Treiber auch noch) Kernel-panics/Bluescreens um die Ohren fliegen werden.
Oder sie nutzen halt Projekte wie PF_RING.

-
Oder die 99.99% der Entwickler die Software schreiben wo es vollkommen egal ist halten einfach den Rand, anstatt dass sie versuchen ihre Kollegen zu missionieren.
Ich möchte nicht wissen wie viel Geld vernichtet wurde durch schlechte Ratschläge wie "mach mal asynchron weil sonst is voll langsam". Und dann sitzt Otto-Normalprogrammierer da und programmiert sich nen Ast ab für den < 1 call/second Server. Was sonst in 10 Minuten erledigt gewesen wäre.
-
Wären 2 Threads pro Client nicht angebrachter, da TCP Sockets full duplex sind?
-
hustbaer schrieb:
Mein Vorschlag wäre pro Verbindung einen Thread zu nehmen, und dann alles mit synchronem IO zu machen.
Super einfach und wer behaupten möchte dass es von der Performance her schlecht ist, der soll es mir erstmal beweisen
Danke, da ich bisher wenig mit Threads gearbeitet habe. Kannst du es mir bitte etwas genauer erläutern?
Gruß
-
socket = socket() bind(socket) listen(socket) while(true) { new_socket = accept(socket) new_thread(new_socket); } void new_thread(socket) { recv(socket); send(socket); ... closesocket(socket); }
-
Danke für die Antwort.
Achso logich der Thread bleibt beim ersten resv hängen aber dafür wird dann der nachfolgende Thread ausgeführt.
kapiert!
-
^ Oder ?
Also wenn anschließend ein zweiter Thread kommen würde. Würde dann der erste bei resv einen blocking call erhalten und der nächste würde ausgeführt werden?
Danke Gruß