Timed Semaphore in linux


  • Mod

    top ist kein Tool zum Programmprofiling.



  • Ich habs jetzt etwas näher beleuchtet.

    Ich habe die Semaphore falsch benutzt, denn die verlangt keine relative Zeit, sondern eine absolute fuer ihr TimeOut. Aus meiner Sicht völlig behämmert, da es gefühlte x-Mio Zeitbibliotheken gibt...

    errno liefert den Wert 110 ( ETIMEDOUT ).

    Das bedeutet laut manual, dass die absolute Zeit bereits vergangen ist, wenn ich
    sem_timedwait aufrufe.

    Ich berechne das absolute Timeout jetzt folgendermaßen:

    unsigned int uiTimeOut = unsigned( waittime );   // timeout in msec
    
    			struct timespec CurrentTime;
    			clock_gettime( CLOCK_REALTIME, &CurrentTime );
    
    			struct timespec TimeOut = CurrentTime;
    			TimeOut.tv_nsec += uiTimeOut * 1000000;
    			TimeOut.tv_sec += TimeOut.tv_nsec  / 1000000000;
    			TimeOut.tv_nsec %= 1000000000;
    
    			std::cout << CurrentTime.tv_sec << " " << CurrentTime.tv_nsec << std::endl;
    			std::cout << TimeOut.tv_sec << " " << TimeOut.tv_nsec << std::endl;
    
    			int res = sem_timedwait( &hSem, &TimeOut );
    

    Der Fehlercode tritt aber immer noch auf. Nehme ich die falsche Zeitbibliothek? Sind meine Berechnungen falsch?



  • Der Fehlercode ist scheinbar in Ordnung. Die Timeoutzeit passt jetzt. Hab es mal mit 10sec und mal 500ms versucht und beide Wartezeiten passen jetzt.



  • It0101 schrieb:

    Der Fehlercode ist scheinbar in Ordnung. Die Timeoutzeit passt jetzt. Hab es mal mit 10sec und mal 500ms versucht und beide Wartezeiten passen jetzt.

    Errno ist nur gültig, wenn sem_timedwait auch -1 zurückgegeben hat. Ist das der Fall?



  • Ansonsten guck dir doch einfach die Manpage (man 3 sem_wait) an. Da ist auch Beispielcode für sem_timedwait inklusive Errorhandling zu finden.



  • kotnascher schrieb:

    It0101 schrieb:

    Der Fehlercode ist scheinbar in Ordnung. Die Timeoutzeit passt jetzt. Hab es mal mit 10sec und mal 500ms versucht und beide Wartezeiten passen jetzt.

    Errno ist nur gültig, wenn sem_timedwait auch -1 zurückgegeben hat. Ist das der Fall?

    Ja. sem_timedwait liefert nachwievor -1 und der errorcode ist 110. Er wartet aber jetzt korrekt die gewünschte Zeit ( wenns kein Signal gibt ). Wie das mit dem Errorcode zusammenpasst, ist mir auch noch nicht ganz klar.



  • Ich habe jetzt mal mein "Problem" reduziert.

    Gebaut unter Kubuntu 12.10 im Release mit g++ und O3

    #include <semaphore.h>
    #include <errno.h>
    #include <sys/time.h>
    #include <iostream>
    
    int main( int argc, char **argv )
    {
    	sem_t hSem;
    	sem_init( &hSem, 0, 0 );
    	long waittime = 1;
    
    	while ( true )
    	{
    		struct timespec CurrentTime;
    		clock_gettime( CLOCK_REALTIME, &CurrentTime );
    		struct timespec TimeOut = CurrentTime;
    		TimeOut.tv_nsec += waittime * 1000000;
    		TimeOut.tv_sec += TimeOut.tv_nsec  / 1000000000;
    		TimeOut.tv_nsec %= 1000000000;
    		int res = sem_timedwait( &hSem, &TimeOut );
    		//std::cout << "Semaphore result: " << res << " " << errno << std::endl;
    
    	}
    	sem_destroy( &hSem );
    	return 0;
    }
    

    Ausgabe habe ich mal bei 1000ms ausgeben lassen, und da ist res = -1 und errno = 110 ( TIMEOUT ).
    Bei 1000ms liegt keine Last vor. Ab 10ms ist sie fuer "top" messbar, mit fallender Wartezeit steigt die Last.

    Nachwievor macht das System auch in der Form "Last" ( top ) was ich so nicht erwarten würde. Insbesondere wenn man mehrere Threads von der Sorte hat, addiert sich die Last.

    Mach ich hier noch was falsch?
    liegts an meiner Hardware? ( bescheidenes AMD DualCore Notebook )
    liegts am Linux? ( Kubuntu 12.10 )
    liegts am Compileprozess? ( besondere Flags notwendig )



  • Was genau ist das Problem?

    for (;;)
         usleep(10);
    

    erzeugt bei mir auch 6% CPU-Last.



  • semiprof schrieb:

    Was genau ist das Problem?

    for (;;)
         usleep(10);
    

    erzeugt bei mir auch 6% CPU-Last.

    1. Hast du schonmal mit 10 Threads gearbeitet? Ich jedenfalls würde das gern 😉

    2. Unter Windows macht ein 1ms Wait mit der Semaphore keine Last, selbst 10 wartende Threads sind dort nicht messbar. Ich erwarte, dass Linux das genauso hinbekommt.

    Es muss dafür ne bessere Lösung geben. Ich bin schließlich nicht der einzige der unter Linux mehrere Threads betreiben will, bzw. es tut.

    Vielleicht kann ich auch meine Konstruktion so umbauen, dass die wartende Semaphore nicht notwendig ist.

    Es handelt sich bei meinen Threads im wesentlichen um Sockethandler. D.h. die beschäftigen sich mit select, accept, recv und send.

    Insbesondere bei den Threads, die Sockets lesen sind natürlich längere Wartezeiten nicht akzeptabel, da sie die Latenz in die Höhe schrauben. Es ist in dem Fall so, dass dieser besagte Thread nicht nur auf ein recv wartet sondern auch auf serverinterne Messages aus einer Queue.

    Die Semaphore soll auch nur dann warten, wenn der Thread nichts zu tun hatte ( nichts received und auch nichts aus der internen Queue verarbeitet ).



  • It0101 schrieb:

    Es muss dafür ne bessere Lösung geben. Ich bin schließlich nicht der einzige der unter Linux mehrere Threads betreiben will, bzw. es tut.

    Das Problem ist, das deine Lastmessungen utter crap sind. (Wie Linus sagen würde)
    Es gibt da tausend versteckte Effekte, z.B. taktet Linux die CPU runter, wenn nichts gemacht wird.

    Nochmal ein Vergleich (10ms, 100000 Durchgänge):

    semaphore-wait      : 0.54s user 1.06s system 16% cpu 9.709 total
    usleep              : 0.11s user 1.42s system 15% cpu 9.527 total
    usleep+clock_gettime: 0.51s user 1.03s system 15% cpu 9.753 total
    

    usleep ist genauso lahm wie semaphore. Folgt daraus, dass usleep ein Busy-Wait ist?

    It0101 schrieb:

    1. Hast du schonmal mit 10 Threads gearbeitet? Ich jedenfalls würde das gern 😉

    Hast du schonmal mit 10 Threads einen Unterschied gemessen? Nicht %CPU sondern absolut?

    Nein? Dann mach bevor du irgendwelchen Mist in fehlerhafte Messungen reininterpretierst.


  • Mod

    Ich habe es schon einmal gesagt und ich sage es noch einmal:

    SeppJ schrieb:

    top ist kein Tool zum Programmprofiling.

    Nimm einen Profiler, wenn du wissen möchtest, wie viel CPU-Last dein Programm wirklich erzeugt.



  • Das ist mir schon klar, aber wenn ich 10 Threads starte und dort zehnmal immer 1ms warten lasse, dann habe ich eben 10 x 2% = 20% CPU-Last.

    Irgendwann entkräftet sich auch dein Argument mit dem Profiler. 20% Last, selbst wenn sie "nur" von "top" gemeldet werden, kann man wohl nicht mehr wegdiskutieren und wenn in Wirklichkeit tatsächlich keine Last vorliegt, aber "top" 20% anzeigt, dann muss irgendwann einfach mal zu der Erkenntnis kommen, dass Linux doch nicht so der Oberbrüller ist.



  • It0101 schrieb:

    Es muss dafür ne bessere Lösung geben. Ich bin schließlich nicht der einzige der unter Linux mehrere Threads betreiben will, bzw. es tut.

    Vielleicht kann ich auch meine Konstruktion so umbauen, dass die wartende Semaphore nicht notwendig ist.

    Es handelt sich bei meinen Threads im wesentlichen um Sockethandler. D.h. die beschäftigen sich mit select, accept, recv und send.

    Insbesondere bei den Threads, die Sockets lesen sind natürlich längere Wartezeiten nicht akzeptabel, da sie die Latenz in die Höhe schrauben. Es ist in dem Fall so, dass dieser besagte Thread nicht nur auf ein recv wartet sondern auch auf serverinterne Messages aus einer Queue.

    Die Semaphore soll auch nur dann warten, wenn der Thread nichts zu tun hatte ( nichts received und auch nichts aus der internen Queue verarbeitet ).

    Dein Konzept ist falsch. Es macht keinen Sinn auch nur kurz zu warten. Entweder der Thread hat was zu tun oder er wartet, dass er was zu tun bekommt. Und wenn er wartet, dann sollte es kein Busy-Wait sein und auch kein ein wenig Busy-Wait indem Du nur mal immer kurz wartest, so wie Du es versuchst.

    Dein Thread soll also auf Daten vom Socket und gleichzeitig auf eine Semaphore warten. Das geht so nicht. Du kannst unter Linux/Unix auf mehrere Sachen Warten, wenn es Filedeskriptoren sind. Dann kannst Du mit select oder poll (oder unter Linux epoll) auf Ereignisse warten. Der Thread kann dann mit einem unendlichen Timeout warten. Er wird dann erst geweckt, wenn wirklich was zu tun ist.

    Das löst man normalerweise so, dass man in den select oder poll neben dem Socket-Deskriptor noch eine Pipe dazu packt. Wenn ein anderer Thread dann dem Thread eine Nachricht schicken will, schreibt er in die Pipe und unterbricht das Warten. Der wartende Thread kann dann darauf reagieren.

    Wenn Du dann 10 oder 100 oder 1000 Threads hast, die alle auf select oder poll warten, dann erzeugt das keine Last.


  • Mod

    It0101 schrieb:

    Das ist mir schon klar, aber wenn ich 10 Threads starte und dort zehnmal immer 1ms warten lasse, dann habe ich eben 10 x 2% = 20% CPU-Last.

    Nein. Milchmädchenrechnung.

    Irgendwann entkräftet sich auch dein Argument mit dem Profiler. 20% Last, selbst wenn sie "nur" von "top" gemeldet werden, kann man wohl nicht mehr wegdiskutieren

    Doch. Milchmädchenrechnung. Wenn dir wirklich klar wäre, was top anzeigt, dann würdest du das auch verstehen.

    und wenn in Wirklichkeit tatsächlich keine Last vorliegt, aber "top" 20% anzeigt, dann muss irgendwann einfach mal zu der Erkenntnis kommen, dass Linux doch nicht so der Oberbrüller ist.

    Nein, dann kommt man zu der Erkenntnis, dass top kein Profilingwerkzeug ist 🙄 .



  • It0101 schrieb:

    dann muss irgendwann einfach mal zu der Erkenntnis kommen, dass Linux doch nicht so der Oberbrüller ist.

    Ja, du hast Recht. Linux ist total langsam. Wenn es um Rechenpower und Stromsparen geht, ist Windows viel viel besser.

    *ich gebs auf*



  • SeppJ schrieb:

    Nein, dann kommt man zu der Erkenntnis, dass top kein Profilingwerkzeug ist 🙄 .

    Dann kommt wenn schon denn schon zur Erkenntnis, das zumindest die CPU-Werte im Top eigentlich garnix aussagen.



  • tntnet schrieb:

    Wenn Du dann 10 oder 100 oder 1000 Threads hast, die alle auf select oder poll warten, dann erzeugt das keine Last.

    Genau das ist im Prinzip auch die Lösung die ich anstrebe. Die ist rein logisch und technisch auch sauberer als das Mini-Wait, was ich derzeit verwende.


Anmelden zum Antworten