Ich fürchte, ich mache hier "das Falsche", wenn ja, lösche mich und ich entschuldige mich. Insbesondere sehe ich nicht, wie ich die hübschen kleinen Anmerkungen erstelle, die einige Leute erstellt haben. Ich habe jedoch viele Bedenken / Beobachtungen zu diesem Thema.
1) Das kommentierte Element im Pseudocode in einer der populären Antworten
result = query( "select smurfs from some_mushroom" );
// twiddle fingers
go_do_something_with_result( result );
ist im Wesentlichen falsch. Wenn der Thread rechnet, dreht er nicht mit den Daumen, sondern erledigt die notwendige Arbeit. Wenn andererseits nur auf den Abschluss der E / A gewartet wird und dann keine CPU-Zeit verwendet wird, besteht der springende Punkt der Thread-Steuerungsinfrastruktur im Kernel darin, dass die CPU etwas Nützliches findet. Die einzige Möglichkeit, "Daumen zu drehen", wie hier vorgeschlagen, besteht darin, eine Abfrageschleife zu erstellen, und niemand, der einen echten Webserver codiert hat, ist dazu nicht in der Lage.
2) "Threads sind hart", macht nur im Zusammenhang mit dem Datenaustausch Sinn. Wenn Sie im Wesentlichen unabhängige Threads haben, wie dies bei der Verarbeitung unabhängiger Webanforderungen der Fall ist, ist das Threading trivial einfach. Sie codieren einfach den linearen Ablauf für die Verarbeitung eines Jobs und wissen, dass mehrere Anforderungen verarbeitet werden wird effektiv unabhängig sein. Persönlich würde ich es wagen, dass für die meisten Programmierer das Erlernen des Schließ- / Rückrufmechanismus komplexer ist als das einfache Codieren der Top-to-Bottom-Thread-Version. (Aber ja, wenn Sie zwischen den Threads kommunizieren müssen, wird das Leben sehr schnell sehr schwer, aber dann bin ich nicht davon überzeugt, dass der Schließ- / Rückrufmechanismus dies wirklich ändert. Er schränkt nur Ihre Optionen ein, da dieser Ansatz mit Threads immer noch erreichbar ist Wie auch immer, das '
3) Bisher hat niemand echte Beweise dafür vorgelegt, warum eine bestimmte Art von Kontextwechsel mehr oder weniger zeitaufwändig wäre als jede andere Art. Meine Erfahrung bei der Erstellung von Multitasking-Kerneln (im kleinen Maßstab für eingebettete Controller nichts Besonderes als ein "echtes" Betriebssystem) legt nahe, dass dies nicht der Fall wäre.
4) Alle Abbildungen, die ich bisher gesehen habe, um zu zeigen, wie viel schneller Node ist als andere Webserver, sind schrecklich fehlerhaft. Sie sind jedoch auf eine Weise fehlerhaft, die indirekt einen Vorteil veranschaulicht, den ich definitiv für Node akzeptieren würde (und es ist keineswegs unbedeutend). Der Knoten sieht nicht so aus, als müsste er optimiert werden (und erlaubt es auch nicht). Wenn Sie ein Thread-Modell haben, müssen Sie genügend Threads erstellen, um die erwartete Last zu bewältigen. Wenn Sie dies schlecht machen, erhalten Sie eine schlechte Leistung. Wenn zu wenige Threads vorhanden sind, befindet sich die CPU im Leerlauf, kann jedoch keine weiteren Anforderungen akzeptieren, erstellt zu viele Threads, und Sie verschwenden Kernelspeicher. In einer Java-Umgebung verschwenden Sie auch den Hauptspeicher des Heapspeichers . Für Java ist die Verschwendung von Heap der erste und beste Weg, um die Leistung des Systems zu verbessern. Weil eine effiziente Speicherbereinigung (derzeit könnte sich dies mit G1 ändern, aber es scheint, dass die Jury zumindest Anfang 2013 noch nicht über diesen Punkt informiert ist) davon abhängt, dass viel Ersatzhaufen vorhanden ist. Es gibt also das Problem: Optimieren Sie es mit zu wenigen Threads, Sie haben inaktive CPUs und einen schlechten Durchsatz, optimieren Sie es mit zu vielen und es bleibt auf andere Weise hängen.
5) Es gibt eine andere Art und Weise, wie ich die Logik der Behauptung akzeptiere, dass der Ansatz von Node "von Natur aus schneller ist", und das ist dies. Die meisten Thread-Modelle verwenden ein zeitlich begrenztes Kontextwechselmodell, das über dem geeigneteren (Wertbeurteilungsalarm :) und effizienteren (kein Wertbeurteilungs-) Präventivmodell liegt. Dies geschieht aus zwei Gründen: Erstens scheinen die meisten Programmierer die Prioritätsvoraussetzung nicht zu verstehen, und zweitens, wenn Sie das Threading in einer Windows-Umgebung lernen, ist das Timeslicing vorhanden, ob Sie es mögen oder nicht (dies verstärkt natürlich den ersten Punkt Insbesondere in den ersten Versionen von Java wurde bei Solaris-Implementierungen die Prioritätsvoraussetzung und bei Windows die Zeiteinteilung verwendet, da die meisten Programmierer nicht verstanden und sich darüber beschwert haben, dass "Threading in Solaris nicht funktioniert". Sie haben das Modell überall auf Zeitscheibe geändert. Unter dem Strich führt Timeslicing zu zusätzlichen (und möglicherweise unnötigen) Kontextwechseln. Jeder Kontextwechsel benötigt CPU-Zeit, und diese Zeit wird effektiv von der Arbeit entfernt, die für den eigentlichen Job ausgeführt werden kann. Die Zeit, die aufgrund von Zeitscheiben in den Kontextwechsel investiert wird, sollte jedoch nicht mehr als einen sehr kleinen Prozentsatz der Gesamtzeit betragen, es sei denn, es passiert etwas ziemlich Außergewöhnliches, und es gibt keinen Grund, warum ich davon ausgehen kann, dass dies in einem Fall der Fall ist einfacher Webserver). Ja, die überschüssigen Kontextwechsel, die mit Timeslicing verbunden sind, sind ineffizient (und diese treten in nicht auf und diese Zeit wird effektiv von der Arbeit entfernt, die an der eigentlichen Arbeit erledigt werden kann. Die Zeit, die aufgrund von Zeitscheiben in den Kontextwechsel investiert wird, sollte jedoch nicht mehr als einen sehr kleinen Prozentsatz der Gesamtzeit betragen, es sei denn, es passiert etwas ziemlich Außergewöhnliches, und es gibt keinen Grund, warum ich davon ausgehen kann, dass dies in einem Fall der Fall ist einfacher Webserver). Ja, die überschüssigen Kontextwechsel, die mit Timeslicing verbunden sind, sind ineffizient (und diese treten in nicht auf und diese Zeit wird effektiv von der Arbeit entfernt, die an der eigentlichen Arbeit erledigt werden kann. Die Zeit, die aufgrund von Zeitscheiben in den Kontextwechsel investiert wird, sollte jedoch nicht mehr als einen sehr kleinen Prozentsatz der Gesamtzeit betragen, es sei denn, es passiert etwas ziemlich Außergewöhnliches, und es gibt keinen Grund, warum ich davon ausgehen kann, dass dies in einem Fall der Fall ist einfacher Webserver). Ja, die überschüssigen Kontextwechsel, die mit Timeslicing verbunden sind, sind ineffizient (und diese treten in nicht aufKernel- Threads in der Regel übrigens), aber der Unterschied beträgt einige Prozent des Durchsatzes, nicht die Art von Ganzzahlfaktoren, die in den Leistungsansprüchen enthalten sind, die häufig für Node impliziert werden.
Wie auch immer, ich entschuldige mich dafür, dass alles lang und unruhig ist, aber ich habe wirklich das Gefühl, dass die Diskussion bisher nichts bewiesen hat, und ich würde mich freuen, von jemandem in einer dieser beiden Situationen zu hören:
a) eine echte Erklärung, warum Node besser sein sollte (über die beiden oben beschriebenen Szenarien hinaus, von denen das erste (schlechte Abstimmung) meiner Meinung nach die wahre Erklärung für alle Tests ist, die ich bisher gesehen habe ] Je mehr ich darüber nachdenke, desto mehr frage ich mich, ob der von einer großen Anzahl von Stapeln verwendete Speicher hier von Bedeutung sein könnte. Die Standardstapelgrößen für moderne Threads sind in der Regel ziemlich groß, aber der von a zugewiesene Speicher Ein geschlossenes Ereignissystem wäre nur das, was benötigt wird.
b) ein echter Benchmark, der dem Thread-Server Ihrer Wahl tatsächlich eine faire Chance bietet. Zumindest auf diese Weise müsste ich aufhören zu glauben, dass die Behauptungen im Wesentlichen falsch sind;> ([Bearbeiten] das ist wahrscheinlich eher stärker als beabsichtigt, aber ich bin der Meinung, dass die Erklärungen für Leistungsvorteile bestenfalls unvollständig sind, und die Die gezeigten Benchmarks sind nicht zumutbar.
Prost, Toby
select()
ist schneller als das Austauschen von Thread-Kontexten.