Maximale Anzahl (p)threads?



  • Hi,

    ich möchte eine Anfrage an ca. 60 Server anschicken, nur je ca. 20 bytes request und je ca. 20 bytes response (Timeout ca. 5 sekunden). Das ganze sollte aber möglichst schnell gehen, daher will ich nicht alles einzeln machen, sondern mit pthreads (am besten einen pthread pro Server).

    a.) Halten das auch nicht ganz so neue CPUs aus, 60 pthreads gleichzeitig zu starten?
    b.) Wie viele pthreads kann ich maximal starten?
    c.) Ist diese Idee sinnvoll oder eher nicht?

    Vielen dank für Eure Antworten im Voraus!
    Johannes



  • erstmal: zum übertragen von 20byte einen thread zu starten, ist völlig übertrieben. da verlierst du mehr zeit beim erzeugen des threads und für deren verwaltung als für die io...
    anzahl: unter linux 2^31 auf ia32 maschinen. andere 32bit systeme ähnliche größenordnungen.



  • Ich finde es sinnvoll. Solange es nicht zeitkritisch ist, nimm die am schnellsten zu programmierende Methode, was meiner Meinung nach die Thread-Methode ist (eben auch weil du dann ein synchrones Timeout-warten hast).



  • ad a) ja, 60 threads packt ein C64 🕶
    ad b) soweit ich weiss plattformabhängig. üblicherweise sollte es ab 1000 oder 2000 die ersten probleme geben (vorausgesetzt genug RAM is da, sonst früher). auf 64 bit systemen vermutlich später erst (wieder: nur wenn genung RAM da ist).

    und 2^31 geht garantiert auf keiner ia32 box. ein thread braucht normalerweise mindestens eine page stack, von daher wären wir schon auf max. 2^20 herunten (bei 4K pages). realistisch betrachtet kannst du aber froh sein wenn du irgendwie auf 10K threads kommst - wobei ich nichtmal garantieren kann dass das noch irgendwie drin ist.

    ad c) in diesem speziellen fall, und wenn du mit threads umgehen kannst, dann ist die idee unter umständen nicht ganz verkehrt.

    grund: netzwerke sind langsam, WAN ganz speziell. die zeit die du durch das anlegen von 60 threads verlierst ist minimal im vergleich dazu was das warten auf die antworten dauern wird (auch wenn es parallel passiert). bei 60 servern kannst du davon ausgehen dass meist das timeout irgendwo zuschlagen wird, d.h. der ganze spass wird einfach oft 5 sekunden dauern und nicht kürzer.

    wenn das ganze skalieren soll ist die idee nichtmehr so gut, da du eben probleme bekommen könntest wenn du mal eben 5000 threads rausstarten willst.

    in dem fall wäre asynchrones io angebracht, ist aber nicht trivial wenn man damit noch nie gearbeitet hat. andrerseits machen anfänger auch gerne fehler mit threads, die dann dummerweise oft sehr schwer festzunageln sind. von daher wäre es vermutlich wirklich geschickter asynchrones IO zu verwenden.

    Boost.ASIO wäre eine library die das halbwegs schön wegkapselt, so dass man mit den lästigsten aspekten nichtsmehr zu tun hat. ausserdem sind da auch bereits dinge implementiert wie ein timeout bei connect, was sonst u.u. auch erstmal ein paar stunden doku lesen bedeutet wenn du es selbst implementieren willst.



  • @hustbaer pae. dann kann man auch auf einem 32bit system genug platz haben. (bevor du fragst, nein ich so etwas noch nicht gemacht.)
    die 2^31 sind die max anzahl, die an ids für threads erzeugt werden können, also genau wie die maximale prozesszahl eher ein theoretischer wert.

    asynchrone io ist hier die definitiv die sinnvollste variante.



  • Nimm asynchrones IO und wenn du dann noch das Bedürfnis hast mit Threads zu hantieren, kombiniere beides, aber das macht eigentlich nur bei large-scale Serveranwendungen sinn, wo 10k+ Verbindungen offen sind (darfst natürlich auch schon bei weniger einsetzen, bevor jetzt gemeckert wird).



  • @ghorst: mir ist klar dass es ein theoretischer wert war, aber dadurch wird er nicht sinnvoller 😉
    und ich kenne pae, ja. bloss ob man das für stacks so ohne weiteres einsetzen kann...?



  • warum pthreads?



  • voidpointer schrieb:

    Hi,

    ich möchte eine Anfrage an ca. 60 Server anschicken, nur je ca. 20 bytes request und je ca. 20 bytes response (Timeout ca. 5 sekunden). Das ganze sollte aber möglichst schnell gehen, daher will ich nicht alles einzeln machen, sondern mit pthreads (am besten einen pthread pro Server).

    a.) Halten das auch nicht ganz so neue CPUs aus, 60 pthreads gleichzeitig zu starten?

    da 99% der zeit vermutlich gewartet wird, ist das der cpu egal.

    b.) Wie viele pthreads kann ich maximal starten?

    je nach OS muss man mit ca 1MB pro thread rechnen. 1000 duerften also 1GB ram ziehen.

    c.) Ist diese Idee sinnvoll oder eher nicht?

    eher nicht sinnvoll. mach dir lieber nur 60 sockets auf, schick die daten ab und lauf dann ein paar mal durch alle noch offenen sockets und teste ob was drinnen steht (sieh ioctlsocket + FIONREAD)
    ein taskswitch dauert tausend mal laenger als durchs array zu laufen 😉



  • hustbaer schrieb:

    und ich kenne pae, ja. bloss ob man das für stacks so ohne weiteres einsetzen kann...?

    wenn man das für ia32-systeme übliche 36bir pae ansetzt, ist der stack wohl eher die letzte sorge, die man hat. 32 byte pro thread als gesamtmenge an speicher, ist leider alles andere als berauschend. daher eher ohne stack und eigenen speicher. also ein bisschen rumgeschubse von registern, was aber beim ia32 auch nicht wirklich spaß macht. wäre aber sicher mal eine nette herausforderung das zu schaffen. (also für den fall, dass der scheduler da nicht vorher selbstmord begeht.)



  • Die Anzahl dürfte auch start OS Abhängig sein aber meines Wissens bekommt unter Linux jeder Thread eine eigenen PID und die PIDs müssen alle in 16bit passen -> 2¹⁶ Threads für Linux wenn sonst nichts läuft



  • da irrst du. die aktuelle linux pthread-implementierung unterstützt 2^31 threads (theoretisch).
    zum nachlesen hier: http://people.redhat.com/drepper/nptl-design.pdf



  • Vielen Dank für Eure Einschätzungen, ich denke, ich werde es mit asynchronuos IO versuchen - ganz schnell hintereinander jedem Server was senden und dann kriegen die alle ein lesendes select() mit Timeout hinterhergeschmissen (da select() ja kein polling ist, sollte das auch gut verkraftbar sein).

    Wenn ich übrigens auf meine Prozessliste schaue, werden die nie größer als ca. 32.000 (fangen danach wieder unten an), was zumindest auf meinem SuSE Linux 10.3 für signed 16 bit spricht.



  • voidpointer schrieb:

    Vielen Dank für Eure Einschätzungen, ich denke, ich werde es mit asynchronuos IO versuchen - ganz schnell hintereinander jedem Server was senden und dann kriegen die alle ein lesendes select() mit Timeout hinterhergeschmissen (da select() ja kein polling ist, sollte das auch gut verkraftbar sein).

    Wenn ich übrigens auf meine Prozessliste schaue, werden die nie größer als ca. 32.000 (fangen danach wieder unten an), was zumindest auf meinem SuSE Linux 10.3 für signed 16 bit spricht.

    So schauts aus, signed 16 bit wird auch benutzt (standardmäßig).



  • Hi,

    ich verstehe nicht so ganz, wozu du jedesmal einen Fred aufmachen willst. Hast Du 2^31 Netztwerkkarten? Du hast doch in jedem Fall eine Engstelle, durch die es nur langsam und seriell durchgeht, und das ist der Netzzugriff. Das ist wie mit der Baustelle auf der Autobahn. Es bringt für den Gesamtdurchsatz nichts, wenn Du von 6 Spuren auf 8 Spuren erweiterst, wenn am Ende eine einspurige Baustelle ist. Genau so ist es mit Deinem Problem. Das langsamste ist sowieso das Netz, und das ist nunmal seriell.

    Gruß Mümmel



  • @muemmel:
    Die Netzwerkkarte ist hier sicher nicht die Engstelle, denn es dauert unheimlich lange bis so ein Server im Internet antwortet.
    Und das Netz ist nicht in dem Sinn "seriell" dass man nicht mehrere Requests an mehrere Server gleichzeitig ausständig haben könnte. Das wäre ja ne Katastrophe wenn das nicht ginge. Es macht also durchaus Sinn quasi gleichzeitig mehrere Requests abzusetzen. Die Frage ist nur das wie, also ob mit Threads oder asynchronem IO oder nonblocking IO oder...



  • Hi,

    ist mir schon klar, daß die eigentliche Antwort viel langsamer ist als die Netzwerkkarte, denn die kann ja problemlos die volle vom Protokoll mögliche Datenrate.
    Aber INNERHALB Deines Rechners ist die Netzwerkkarte mit Sicherheit das langsamste Teil, denn sie muß von der heutezutage doch recht gewaltigen Geschwindigkeit des Prozessors runter auf das vergleichsweise langsame Tempo des Netzes transformieren.
    Sicher, das Netz selber ist nicht seriell, aber die Netzwerkkarte schon. Da geht immer nur ein Paket nach dem anderen durch. Was bringt es da, wenn die einzelnen Prozesse Schlange stehen, und warten bis sie was zu tun bekommen.
    Nicht vergessen, daß ein zusätzlicher Fred immer wesentlich mehr Overhead hat als nur einfach eine Funktion aufrufen. Wenn mehrere Prozesse, dann höchstens dahingehend, daß einer Deine Netzwerkkarte abfragt und wenn was da ist das dann an den zweiten übergibt zum Verarbeiten. Aber viele Prozesse starten lohnt schon deshalb nicht, weil Deine Maschine mit hoher Wahrscheinlichkeit nicht mehr als maximal 4 Kerne hat. Du vertust also im Endeffekt die Zeit blos damit, die Maschine von Fred zu Fred springen zu lassen statt zu arbeiten.
    Einen wirklichen Sinn macht es nur dann, wenn dadurch die eigentliche Programmierung für Dich einfacher und übersichtlicher wird. Aber leisungsmäßig holst Du da bestimmt nichts raus.

    Gruß Mümmel



  • muemmel, deiner Argumentation kann ich nicht ganz folgen. Vor allem was die Netzwerkkarte angeht. Dass mehrere Threads/Prozesse wohl nicht die beste Lösung ist wurde schon mehrfach geschrieben, hat aber mit der Netzwerkkarte nun garnix zu tun. Genauso eigentlich nicht mit der Zeit die beim Context-Switching draufgeht. Deine Argumentation "brauchste eigentlich garnicht" was Threads angeht lässt sich auch auf viele Bereiche anwenden wo oft Threads eingesetzt werden weil es einfach praktisch ist.



  • @muemmel dir ist schon klar, dass man die Übertragungsgeschwindigkeit einer Netzwerkkarte/-verbindung durch die Bandbreite misst und 60*40=2400Bytes quasiparallel übertragen werden (zusätzliche Bandbreite durch Ethernet, IP und TCP vernachlässigt).



  • Nun muss ich doch fragen, was hier nicht stimmt:

    // (foreach client (c_itr)
    				*c_itr = new Client(s_itr->ip.c_str(), s_itr->port, eUDP);
    
    		// (foreach client (c_itr)
    			(*c_itr)->send("\\status\\",8);
    
    		sleep(3); // TIMEOUT
    	        // (foreach client (c_itr)
                    {
    			char buffer[1024];
                            (*c_itr)->recv(buffer, 1024);
    			puts("done...");
                    }
    
    		// (foreach client (c_itr)
    			delete *c_itr;
    

    Das ist jetzt mehr oder weniger Pseudo-Code. Zuerst connected er zu allen servern der Reihe nach, danach sendet er der Reihe nach jedem Server eine Message. Dann sollte er nach 3 Sekunden von jedem Server die Response empfangen.

    Allerdings hängt er nach ca. 10 von 100 Servern beim recv() fest (dieses Client::recv() macht vorher einen select()-call mit Timeout - aber das Timeout wird hier überschritten und es passiert nichts)...

    Geht das so überhaupt? Kann ich hundert Servern nen Request schicken, so dass die alle ihre Nachrichten auf meinem Socket "speichern"? Ist der Speicherplatz meines Sockets vielleicht begrenzt? (Die Antworten müssten zusammen ca. 30 kb groß sein).

    BTW: Wireshark sagt mir auch nur, dass alle 100 send-Befehle geklappt haben, aber nur die ca. 10 erwähnten Nachrichten eingegangen sind.


Anmelden zum Antworten