13

pthread_once() ve sem_wait() veya dispatch_ * işlevleri gibi POSIX çağrılarını daha iyi/hızlı kullanmanın ne olacağını bilmek istedim, bu yüzden küçük bir test oluşturdum ve sonuçlara şaşırdım (sorular ve sonuçlar en sonunda).Performans testi: sem_t v.s. dispatch_semaphore_t ve pthread_once_t v.s. dispatch_once_t

Test kodunda, aramaları zamanlamak için mach_absolute_time() kullanıyorum. Bunun nano saniyelerle tam olarak eşleşmediği umrumda değil; Değerleri birbirleriyle karşılaştırıyorum, böylece tam zaman birimleri önemli değil, sadece aralık arasındaki farklar. Sonuçlar bölümündeki sayılar tekrarlanabilir ve ortalaması alınmaz; Süreleri ortalaması alabilirdim ama tam sayıları aramıyorum.

test.m (basit konsol uygulaması; kolay derlemek için):

Benim iMac üzerinde
#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; 
} 

(Snow Leopard Sunucusu 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 

alıyorum:

 
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 

MacBook Pro'm (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 

I got:

 
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 

iPhone 3GS 4.0.2 I got:

 

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 

Sorular ve ifadeleri:

  • sem_wait() ve sem_post() zaman yavaş Çekişme altında değil
    • Bu neden böyle?
    • OSX uyumlu API'ları umursamıyor mu? Bunu yavaşlatmaya zorlayan eski bir kod var mı?
    • Bu numaralar neden dispatch_semaphore işlevleriyle aynı değil?
  • sem_wait() ve sem_post() bir fark vardır (olmadığında olarak ne zaman çekişme altında tıpkı yavaş ama çekişme altında değil arasında büyük bir fark olacağını düşündük; beklediğim içinde ne gibi numaralar adlandırılmış semaforları kullanırken displacement_semaphore kodu)
  • sem_wait() ve sem_post() daha yavaştır.
    • Neden? Bu, semaforun süreçler arasında senkronize edilmesi gerektiğinden mi? belki bunu yaparken daha fazla bagaj var. değil çekişme altında (burada hiçbir sürpriz elma bu çok isteyerek olur çünkü) ne zaman
  • dispatch_semaphore_wait() ve dispatch_semaphore_signal() hızlı deli.
  • dispatch_semaphore_wait() ve dispatch_semaphore_signal() Neden bu kadar yavaş olduğunda çekişme
    • altında daha yavaş sem_wait() ve sem_post() 3 kata var? bu bana mantıklı gelmiyor. Bu çekişme altında sem_t ile eşit olmak için beklerdik.
  • dispatch_once()pthread_once()'den daha hızlı, yaklaşık 10x, neden? Başlıklardan anlatabileceğim tek şey, dispatch_once() ile pthread_once()'dan daha fazla fonksiyon yükü olmamasıdır.

Motivasyon: Ben semaphores için işin yapılması için araçlar 2 takım sunulan veya bir defa çağırır am (Aslında bu arada diğer semafor varyantları bulundu, ancak bir şekilde yetişmiş sürece bu göz ardı eder daha iyi bir seçenek). Sadece bir philips veya flathead ile vidasına vidalanan seçeneği varsa ben gerekirse vida ve flathead tork yoksa, ben philips seçsin (iş için en iyi araçtır bilmek istiyorum vidayı sıkın. : Haliyle

); Ben libdispatch ile yardımcı programları yazmaya başlamak eğer libdispatch henüz çalışma yok diğer işletim sistemlerine port onları mümkün olmayabilir görünüyor ... ama kullanımı çok cazip olduğunu Yaptığım zaman taşınabilirlik ve POSIX çağrıları hakkında endişelenmem gerekmediğinde libdispatch kullanıyorum.

Teşekkürler!

cevap

10

sem_wait() ve sem_post() işlemleri arasında kullanılabilir ağır senkronizasyon tesisleridir. Her zaman çekirdeğe gidiş gelişi içerirler ve muhtemelen iş parçacığının her zaman yeniden planlanması gerekir. Genellikle süreç içi senkronizasyon için doğru seçim değildir. Adlandırılmış varyantların neden anonim olanlardan daha yavaş olacağından emin değilim ...

Mac OS X, Posix uyumluluğu hakkında oldukça iyi ... Ancak, Posix teknik özellikleri çok sayıda isteğe bağlı işlev ve Mac hepsine sahip değil. Gönderiniz aslında pthread_barriers hakkında duyduğum ilk şey, bu yüzden ya nispeten yeni ya da ortak olanları değil. (Son on yıldır pthreads evrimine çok fazla dikkat etmedim.)

Muhtemelen zorlu çekişme altında irsaliyenin parçalarının ayrılmasının sebebi büyük olasılıkla, kapağın altında davranışın döndürme kilitlerine benzemesidir. Görev dağıtıcı çalışan iş parçacığı çok büyük olasılıkla çekişme altında kaynak herhangi döngüsü artık olacak iyimser varsayımı altında kuantumların iyi bir yığın israf ... Shark ile zamanın Biraz kesin söylerdim. Ancak, eve dönüş noktası, çekişme sırasında thrashing'in "optimize edilmesi", programcı zamanının zayıf bir yatırımıdır. Bunun yerine, kodu en iyi duruma getirmek için kodu 'a harcayarak ilk etapta ağır çekişmeyi önleyin.

Eğer gerçekten kitlesel optimum altında etrafında bir semafor koyarak, sizin süreç içinde bir un önlenebilir darboğaz olan bir kaynak varsa. Kendi seri gönderme kuyruğuna koyun ve o sıradaki execatch_async bloklarının olabildiğince fazlasını yapın.

Son olarak, dispatch_once() o spec 've şimdiki işlemciler üzerinde hızlı olacak şekilde uygulanan çünkü pthread_once() daha hızlıdır. Ben referans uygulama pthread senkronizasyon temel öğeler kullanır, ancak ... iyi ... onlar yerine libdispatch iyilik her sağladık şüpheli olarak Muhtemelen Apple, pthread_once() uygulanmasını hızlandırmak olabilir.:-)

+2

dispatch_semaphores'ten beri sem_wait/post ile ilgili iyi bir nokta, bağlamdaki çekirdekle uğraşmak zorunda değilsiniz (bir duh! Gibi görünüyor;); Gömülü sistemler için bir ev yapımı çekirdeğe POSIX uyumluluğu ekledim ve ünite testleri oluşturmak için yararlı engeller buldum. Belirli bir durumu diğerine göre optimize etmeye çalışmıyorum, ancak iş için en iyi araç olan 2 aleti anlamaya çalışıyorum (eğer bir vida veya düz başlı bir vidaya vidalama seçeneğim varsa). .. philips kullanacağım). Motivasyon ile soru güncelleniyor ... –

+0

pthread_once(), "aranan" kontrol için atomiklerle uygulanabilir/uygulanmalıdır; çağrıyı tamamlamayı beklerken evet, diğer konuları engellemek için bir pthread_mutex kullanacağını kabul ediyorum (böyle yaptım). Bu test örneğinde, herhangi bir engelleme yoktur ve bir çekirdek bağlam anahtarına gerek yoktur. Bununla birlikte, neden 10x farkın olduğunu anlamıyorum. Bence libdispatch, posix aramalarından daha fazla optimize edildi. –

+0

başka bir gözlem: anonim semaforlar, başka bir işlemden onları alamadıklarından dispatch_semaphores gibi çalışmalıdır. Engellenmiş parçacıkları engellemeniz veya uyandırmanız gerektiğinde, yalnızca çekirdeğe geçiş yapmanız gerekir (Apple'ın semaforlar için atomik kullandığımız göz önüne alındığında, bu bizim OS'deki semaforlarımız için yaptığım şeydir). –