Question

Je voulais savoir ce serait mieux / plus rapide d'utiliser les appels comme pthread_once() et POSIX sem_wait() ou dispatch_ * fonctions, donc je créé un petit test et je suis surpris des résultats (les questions et les résultats sont à la fin).

Dans le code de test que je utilise en temps les appels mach_absolute_time (). Je ne me soucie pas vraiment que cela ne correspond pas à exactement avec nano-secondes; Je compare les valeurs les unes aux autres de sorte que les unités de temps exactes ne comptent pas, seules les différences entre l'intervalle font. Les chiffres dans la section des résultats sont reproductibles et non en moyenne; Je aurais pu en moyenne les temps mais je ne suis pas à la recherche de chiffres exacts.

test.m (application console simple, facile à compiler):

#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;
}

Sur mon iMac (10.6.4 Snow Leopard Server):

  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

J'obtenir:

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

Sur mon 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

je suis arrivé:

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

sur un iPhone 3GS 4.0.2 je suis arrivé:

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

Questions et déclarations:

  • sem_wait() et sem_post() sont lents lorsqu'ils ne sont pas en conflit
    • Pourquoi est-ce le cas?
    • ne OSX fichait sur les API compatibles? est-il un code existant qui l'obligent à être lent?
    • Pourquoi ne sont pas ces chiffres les mêmes que les fonctions de dispatch_semaphore?
  • sem_wait() et sem_post() sont tout aussi lent lorsque sous affirmation que lorsqu'ils ne sont pas (il y a une différence, mais je pensais que ce serait une énorme différence entre en conflit et non, je pensais des chiffres comme ce qui était dans le code dispatch_semaphore)
  • sem_wait() et sem_post() sont plus lents lorsque vous utilisez les sémaphores nommés.
    • Pourquoi? est-ce parce que le sémaphores doit être synchronisé entre les processus? peut-être il y a plus de bagages quand le faire.
  • dispatch_semaphore_wait() et dispatch_semaphore_signal() sont rapides fou lorsqu'ils ne sont pas en lice (pas de surprise ici puisque Apple est vantant ce lot).
  • dispatch_semaphore_wait() et dispatch_semaphore_signal() sont 3 fois plus lent que sem_wait() et sem_post() lorsque sous contention
    • Pourquoi est-ce si lent? cela ne fait pas de sens pour moi. Je me serais attendu que ce soit à égalité avec la sem_t sous contention.
  • dispatch_once() est plus rapide que pthread_once(), autour de 10x, pourquoi? La seule chose que je peux dire à partir des en-têtes est qu'il n'y a pas de charge d'appel de fonction avec dispatch_once() qu'avec pthread_once().

Motivation: Je suis présenté avec 2 ensembles d'outils pour faire le travail pour sémaphores ou une fois les appels (j'ai trouvé d'autres variantes sémaphores dans l'intervalle, mais je ne tiendrai pas compte les moins élevés comme une meilleure option). Je veux juste savoir ce qui est le meilleur outil pour le travail (si vous avez la possibilité de visser une vis avec un ou philips à tête plate, je choisirais philips si je n'ai pas au couple la vis et Flathead si je dois serrer la vis). Il semble que si je commence à écrire des utilitaires avec libdispatch je ne pourrais pas être en mesure de les porter à d'autres systèmes d'exploitation qui ne sont pas encore ... libdispatch de travail, mais il est si séduisant à utiliser;)

En l'état actuel: Je vais utiliser libdispatch quand je n'ai pas à vous soucier de la portabilité et les appels POSIX quand je fais.

Merci!

Était-ce utile?

La solution

sem_wait () et sem_post () sont des installations de synchronisation de poids lourds qui peuvent être utilisés entre les processus. Ils impliquent toujours des allers-retours au noyau, et nécessitent probablement toujours votre fil à reprogrammée. Ils ne sont généralement pas le bon choix pour la synchronisation en cours. Je ne sais pas pourquoi les variantes nommées seraient plus lentes que les anonymes ...

Mac OS X est en fait assez bonne sur la compatibilité Posix ... Mais les spécifications Posix ont beaucoup de fonctions optionnelles, et le Mac ne les a pas tous. Votre message est en fait le premier que j'ai jamais entendu parler pthread_barriers, donc je suppose qu'ils sont soit relativement récente, ou pas du tout commun. (Je n'ai pas prêté beaucoup d'attention à l'évolution pthreads au cours des dix dernières années.)

La raison pour laquelle la substance d'expédition se désagrège sous contention extrême forcé est probablement parce que sous les couvertures le comportement est similaire aux serrures de rotation. Votre expédition threads de travail sont très gaspillent probablement une bonne partie de leur quanta dans l'hypothèse optimiste que la ressource en lice va être un cycle maintenant disponible ... Un peu de temps avec Shark vous dira à coup sûr. Le point à emporter, cependant, devrait être que « l'optimisation » la raclée lors de contention est un mauvais investissement de temps de programmeur. passer le temps au lieu d'optimiser le code éviter contention lourd en premier lieu.

Si vous avez vraiment une ressource qui est un goulot d'étranglement non évitable au sein de votre processus, mettre un sémaphores autour d'elle est sous-optimale massivement. Mettez-le sur sa propre file d'attente d'envoi de série, et autant que possible des blocs de dispatch_async à exécuter sur cette file d'attente.

Enfin, dispatch_once () est plus rapide que pthread_once () parce qu'il est spec'd et mis en œuvre pour être rapide sur les processeurs actuels. Probablement Apple pourrait accélérer la mise en œuvre pthread_once (), comme je soupçonne l'implémentation de référence utilise des primitives de synchronisation pthread, mais ... eh bien ... ils ont fourni toute la bonté libdispatch à la place. : -)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top