Frage

wollte ich wissen, was besser wäre / schneller POSIX Anrufe wie pthread_once() und sem_wait() oder dispatch_ * Funktionen zu verwenden, so dass ich einen kleinen Test erstellt und bin an den Ergebnissen überrascht (Fragen und Ergebnisse am Ende sind).

Im Testcode Ich bin mit mach_absolute_time (), um die Anrufe Zeit. Ich wirklich nicht darauf, dass dies genau das ist nicht mit Nanosekunden passend auf; Ich vergleiche die Werte miteinander, so dass die genauen Zeiteinheiten nicht Materie, nur die Unterschiede zwischen dem Intervall tun. Die Zahlen im Ergebnisteil sind wiederholbar und nicht gemittelt; Ich konnte die Zeit gemittelt, aber ich bin nicht für genaue Zahlen suchen.

test.m (einfache Konsolenanwendung, einfach zu kompilieren):

#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
#include <semaphore.h>
#include <pthread.h>
#include <time.h>
#include <mach/mach_time.h>  

// *sigh* OSX does not have pthread_barrier (you can ignore the pthread_barrier 
// code, the interesting stuff is lower)
typedef int pthread_barrierattr_t;
typedef struct
{
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int count;
    int tripCount;
} pthread_barrier_t;


int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
{
    if(count == 0)
    {
        errno = EINVAL;
        return -1;
    }
    if(pthread_mutex_init(&barrier->mutex, 0) < 0)
    {
        return -1;
    }
    if(pthread_cond_init(&barrier->cond, 0) < 0)
    {
        pthread_mutex_destroy(&barrier->mutex);
        return -1;
    }
    barrier->tripCount = count;
    barrier->count = 0;

    return 0;
}

int pthread_barrier_destroy(pthread_barrier_t *barrier)
{
    pthread_cond_destroy(&barrier->cond);
    pthread_mutex_destroy(&barrier->mutex);
    return 0;
}

int pthread_barrier_wait(pthread_barrier_t *barrier)
{
    pthread_mutex_lock(&barrier->mutex);
    ++(barrier->count);
    if(barrier->count >= barrier->tripCount)
    {
        barrier->count = 0;
        pthread_cond_broadcast(&barrier->cond);
        pthread_mutex_unlock(&barrier->mutex);
        return 1;
    }
    else
    {
        pthread_cond_wait(&barrier->cond, &(barrier->mutex));
        pthread_mutex_unlock(&barrier->mutex);
        return 0;
    }
}

//
// ok you can start paying attention now...
//

void onceFunction(void)
{
}

@interface SemaphoreTester : NSObject
{
    sem_t *sem1;
    sem_t *sem2;
    pthread_barrier_t *startBarrier;
    pthread_barrier_t *finishBarrier;
}
@property (nonatomic, assign) sem_t *sem1;
@property (nonatomic, assign) sem_t *sem2;
@property (nonatomic, assign) pthread_barrier_t *startBarrier;
@property (nonatomic, assign) pthread_barrier_t *finishBarrier;
@end
@implementation SemaphoreTester
@synthesize sem1, sem2, startBarrier, finishBarrier;
- (void)thread1
{
    pthread_barrier_wait(startBarrier);
    for(int i = 0; i < 100000; i++)
    {
        sem_wait(sem1);
        sem_post(sem2);
    }
    pthread_barrier_wait(finishBarrier);
}

- (void)thread2
{
    pthread_barrier_wait(startBarrier);
    for(int i = 0; i < 100000; i++)
    {
        sem_wait(sem2);
        sem_post(sem1);
    }
    pthread_barrier_wait(finishBarrier);
}
@end


int main (int argc, const char * argv[]) 
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int64_t start;
    int64_t stop;

    // semaphore non contention test
    {
        // grrr, OSX doesn't have sem_init
        sem_t *sem1 = sem_open("sem1", O_CREAT, 0777, 0);

        start = mach_absolute_time();
        for(int i = 0; i < 100000; i++)
        {
            sem_post(sem1);
            sem_wait(sem1);
        }
        stop = mach_absolute_time();
        sem_close(sem1);

        NSLog(@"0 Contention time                         = %d", stop - start);
    }

    // semaphore contention test
    {
        __block sem_t *sem1 = sem_open("sem1", O_CREAT, 0777, 0);
        __block sem_t *sem2 = sem_open("sem2", O_CREAT, 0777, 0);
        __block pthread_barrier_t startBarrier;
        pthread_barrier_init(&startBarrier, NULL, 3);
        __block pthread_barrier_t finishBarrier;
        pthread_barrier_init(&finishBarrier, NULL, 3);

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
        dispatch_async(queue, ^{
            pthread_barrier_wait(&startBarrier);
            for(int i = 0; i < 100000; i++)
            {
                sem_wait(sem1);
                sem_post(sem2);
            }
            pthread_barrier_wait(&finishBarrier);
        });
        dispatch_async(queue, ^{
            pthread_barrier_wait(&startBarrier);
            for(int i = 0; i < 100000; i++)
            {
                sem_wait(sem2);
                sem_post(sem1);
            }
            pthread_barrier_wait(&finishBarrier);
        });
        pthread_barrier_wait(&startBarrier);
        // start timing, everyone hit this point
        start = mach_absolute_time();
        // kick it off
        sem_post(sem2);
        pthread_barrier_wait(&finishBarrier);
        // stop timing, everyone hit the finish point
        stop = mach_absolute_time();
        sem_close(sem1);
        sem_close(sem2);
        NSLog(@"2 Threads always contenting time          = %d", stop - start);
        pthread_barrier_destroy(&startBarrier);
        pthread_barrier_destroy(&finishBarrier);
    }   

    // NSTask semaphore contention test
    {
        sem_t *sem1 = sem_open("sem1", O_CREAT, 0777, 0);
        sem_t *sem2 = sem_open("sem2", O_CREAT, 0777, 0);
        pthread_barrier_t startBarrier;
        pthread_barrier_init(&startBarrier, NULL, 3);
        pthread_barrier_t finishBarrier;
        pthread_barrier_init(&finishBarrier, NULL, 3);

        SemaphoreTester *tester = [[[SemaphoreTester alloc] init] autorelease];
        tester.sem1 = sem1;
        tester.sem2 = sem2;
        tester.startBarrier = &startBarrier;
        tester.finishBarrier = &finishBarrier;
        [NSThread detachNewThreadSelector:@selector(thread1) toTarget:tester withObject:nil];
        [NSThread detachNewThreadSelector:@selector(thread2) toTarget:tester withObject:nil];
        pthread_barrier_wait(&startBarrier);
        // start timing, everyone hit this point
        start = mach_absolute_time();
        // kick it off
        sem_post(sem2);
        pthread_barrier_wait(&finishBarrier);
        // stop timing, everyone hit the finish point
        stop = mach_absolute_time();
        sem_close(sem1);
        sem_close(sem2);
        NSLog(@"2 NSTasks always contenting time          = %d", stop - start);
        pthread_barrier_destroy(&startBarrier);
        pthread_barrier_destroy(&finishBarrier);
    }   

    // dispatch_semaphore non contention test
    {
        dispatch_semaphore_t sem1 = dispatch_semaphore_create(0);

        start = mach_absolute_time();
        for(int i = 0; i < 100000; i++)
        {
            dispatch_semaphore_signal(sem1);
            dispatch_semaphore_wait(sem1, DISPATCH_TIME_FOREVER);
        }
        stop = mach_absolute_time();

        NSLog(@"Dispatch 0 Contention time                = %d", stop - start);
    }


    // dispatch_semaphore non contention test
    {   
        __block dispatch_semaphore_t sem1 = dispatch_semaphore_create(0);
        __block dispatch_semaphore_t sem2 = dispatch_semaphore_create(0);
        __block pthread_barrier_t startBarrier;
        pthread_barrier_init(&startBarrier, NULL, 3);
        __block pthread_barrier_t finishBarrier;
        pthread_barrier_init(&finishBarrier, NULL, 3);

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
        dispatch_async(queue, ^{
            pthread_barrier_wait(&startBarrier);
            for(int i = 0; i < 100000; i++)
            {
                dispatch_semaphore_wait(sem1, DISPATCH_TIME_FOREVER);
                dispatch_semaphore_signal(sem2);
            }
            pthread_barrier_wait(&finishBarrier);
        });
        dispatch_async(queue, ^{
            pthread_barrier_wait(&startBarrier);
            for(int i = 0; i < 100000; i++)
            {
                dispatch_semaphore_wait(sem2, DISPATCH_TIME_FOREVER);
                dispatch_semaphore_signal(sem1);
            }
            pthread_barrier_wait(&finishBarrier);
        });
        pthread_barrier_wait(&startBarrier);
        // start timing, everyone hit this point
        start = mach_absolute_time();
        // kick it off
        dispatch_semaphore_signal(sem2);
        pthread_barrier_wait(&finishBarrier);
        // stop timing, everyone hit the finish point
        stop = mach_absolute_time();

        NSLog(@"Dispatch 2 Threads always contenting time = %d", stop - start);
        pthread_barrier_destroy(&startBarrier);
        pthread_barrier_destroy(&finishBarrier);
    }   

    // pthread_once time
    {
        pthread_once_t once = PTHREAD_ONCE_INIT;
        start = mach_absolute_time();
        for(int i = 0; i <100000; i++)
        {
            pthread_once(&once, onceFunction);
        }
        stop = mach_absolute_time();

        NSLog(@"pthread_once time  = %d", stop - start);
    }

    // dispatch_once time
    {
        dispatch_once_t once = 0;
        start = mach_absolute_time();
        for(int i = 0; i <100000; i++)
        {
            dispatch_once(&once, ^{});
        }
        stop = mach_absolute_time();

        NSLog(@"dispatch_once time = %d", stop - start);
    }

    [pool drain];
    return 0;
}

Auf meinem iMac (Snow Leopard Server 10.6.4):

  Model Identifier: iMac7,1
  Processor Name:   Intel Core 2 Duo
  Processor Speed:  2.4 GHz
  Number Of Processors: 1
  Total Number Of Cores:    2
  L2 Cache: 4 MB
  Memory:   4 GB
  Bus Speed:    800 MHz

ich:

0 Contention time                         =    101410439
2 Threads always contenting time          =    109748686
2 NSTasks always contenting time          =    113225207
0 Contention named semaphore time         =    166061832
2 Threads named semaphore contention time =    203913476
2 NSTasks named semaphore contention time =    204988744
Dispatch 0 Contention time                =      3411439
Dispatch 2 Threads always contenting time =    708073977
pthread_once time  =      2707770
dispatch_once time =        87433

Auf meinem MacbookPro (Snow Leopard 10.6.4):

  Model Identifier: MacBookPro6,2
  Processor Name:   Intel Core i5
  Processor Speed:  2.4 GHz
  Number Of Processors: 1
  Total Number Of Cores:    2 (though HT is enabled)
  L2 Cache (per core):  256 KB
  L3 Cache: 3 MB
  Memory:   8 GB
  Processor Interconnect Speed: 4.8 GT/s

Ich habe:

0 Contention time                         =     74172042
2 Threads always contenting time          =     82975742
2 NSTasks always contenting time          =     82996716
0 Contention named semaphore time         =    106772641
2 Threads named semaphore contention time =    162761973
2 NSTasks named semaphore contention time =    162919844
Dispatch 0 Contention time                =      1634941
Dispatch 2 Threads always contenting time =    759753865
pthread_once time  =      1516787
dispatch_once time =       120778

auf einem iPhone 3GS 4.0.2 Ich habe:

0 Contention time                         =      5971929
2 Threads always contenting time          =     11989710
2 NSTasks always contenting time          =     11950564
0 Contention named semaphore time         =     16721876
2 Threads named semaphore contention time =     35333045
2 NSTasks named semaphore contention time =     35296579
Dispatch 0 Contention time                =       151909
Dispatch 2 Threads always contenting time =     46946548
pthread_once time  =       193592
dispatch_once time =        25071

Fragen und Aussagen:

  • sem_wait() und sem_post() langsam sind, wenn sie nicht unter Konkurrenz
    • Warum ist dies der Fall?
    • ist OSX nicht über kompatible APIs kümmern? gibt es einige Codes Vermächtnis, dass Kräfte dieses langsam sein?
    • Warum nicht diese Zahlen die gleichen wie die dispatch_semaphore Funktionen sind?
  • sem_wait() und sem_post() sind genauso langsam, wenn sie unter Streit als wenn sie nicht (es ein Unterschied gibt, aber ich dachte, dass es ein großer Unterschied zwischen unter Konkurrenz wäre und nicht, ich erwartete Zahlen wie das, was in dem dispatch_semaphore Code war)
  • sem_wait() und sem_post() sind langsamer, wenn benannte Semaphore verwendet wird.
    • Warum? Dies liegt daran, das Semaphor wird zwischen Prozessen synchronisiert? vielleicht gibt es mehr Gepäck, wenn das zu tun.
  • dispatch_semaphore_wait() und dispatch_semaphore_signal() sind verrückt schnell, wenn sie nicht unter Konkurrenz (keine Überraschung, da hier Apfel dies touting viel).
  • dispatch_semaphore_wait() und dispatch_semaphore_signal() sind 3x langsamer als sem_wait() und sem_post(), wenn sie unter Konkurrenz
    • Warum ist das so langsam? dies macht keinen Sinn für mich. Ich habe erwartet, dass dies mit der sem_t unter Konkurrenz gleichauf sein.
  • dispatch_once() ist schneller als pthread_once(), um 10-fach, warum? Das einzige, was ich von den Header sagen kann, ist, dass es keine Funktionsaufruf Belastung mit dispatch_once() ist als bei pthread_once().

Motivation: Ich bin mit 2 Sätzen von Werkzeugen präsentiert die Arbeit für Semaphore oder einmal Anrufe zu erledigen (Ich fand andere Semaphore-Varianten in der Zwischenzeit, aber ich werde diejenigen ignorieren, es sei denn als eine bessere Option gebracht). Ich will nur wissen, was das beste Werkzeug für den Job ist (Wenn Sie die Option zum Einschrauben einer Schraube mit einem Kreuz oder flachköpfig haben, würde ich philips wählen, wenn ich Drehmoment muß die Schraube nicht und flachköpfig wenn ich muß Drehmoment der Schraube). Es scheint, dass, wenn ich schreibe Dienstprogramme mit libdispatch beginne ich nicht in der Lage sein könnte sich auf andere Betriebssysteme zu portieren, die noch nicht haben libdispatch arbeiten ... aber es ist so verlockend zu verwenden;)

Wie es aussieht: Ich werde mit libdispatch, wenn ich nicht zu Sorgen über die Portabilität und POSIX-Anrufe, wenn ich es tue.

Danke!

War es hilfreich?

Lösung

sem_wait () und sem_post () sind Schwergewicht Synchronisationseinrichtungen, die zwischen Prozessen verwendet werden kann. Es handelt sich dabei immer Umläufe an den Kernel, und wahrscheinlich immer erfordern Thread neu geplant werden. Sie sind im Allgemeinen nicht die richtige Wahl für die In-Prozess-Synchronisation. Ich bin mir nicht sicher, warum die genannten Varianten wären langsamer als die anonymen ...

Mac OS X ist eigentlich ziemlich gut über Posix Kompatibilität ... Aber die Posix Spezifikationen haben viele optionale Funktionen und der Mac hat sie nicht alle. Ihr Beitrag ist tatsächlich das erste ich jemals von pthread_barriers gehört habe, so vermute ich, sie sind entweder relativ neu, oder nicht so häufig. (Ich habe nicht viel Aufmerksamkeit auf pThreads Entwicklung in den letzten zehn Jahren gezahlt oder so.)

Der Grund der Versand Sachen auseinander unter erzwungener extremer Konkurrenz fallen, ist wahrscheinlich, weil unter der Decke, das Verhalten zu Spinlocks ähnlich ist. Ihre Dispatch-Worker-Threads sind sehr wahrscheinlich ein gutes Stück ihrer Quanten unter der optimistischen Annahme verschwenden, dass die Ressource unter Konkurrenz nun jeden Zyklus verfügbar sein wird ... ein bisschen Zeit mit Shark würden Ihnen sicher sagen. Der Take-Home Punkt sollte allerdings sein, dass „Optimierung“ die Abreibung während Anstoßes ist eine schlechte Investition von Programmierzeit. Stattdessen verbringt die Zeit, um den Code zu optimieren vermeiden schwerer Streit in dem ersten Platz.

Wenn Sie wirklich eine Ressource, die ein un-vermeidbarer Engpass in Ihrem Prozess ist, setzt eine Semaphore um sie massiv suboptimal sind. Legen Sie es auf seiner eigenen Serien Absende-Warteschlange, und so viel wie möglich dispatch_async Blöcke auf dieser Warteschlange ausgeführt werden.

Schließlich dispatch_once () schneller als pthread_once (), weil es geskilled und umgesetzt werden, schnell auf aktuelle Prozessoren. Wahrscheinlich könnte Apple die pthread_once () Implementierung beschleunigen, da ich die Referenzimplementierung vermuten verwendet Pthread Synchronisation Primitiven, aber ... na ja ... sie haben stattdessen all die libdispatch Güte zur Verfügung gestellt. : -)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top