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()
vesem_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()
vesem_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()
vesem_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()
vedispatch_semaphore_signal()
hızlı deli.dispatch_semaphore_wait()
vedispatch_semaphore_signal()
Neden bu kadar yavaş olduğunda çekişme- altında daha yavaş
sem_wait()
vesem_post()
3 kata var? bu bana mantıklı gelmiyor. Bu çekişme altında sem_t ile eşit olmak için beklerdik.
- altında daha yavaş
dispatch_once()
pthread_once()
'den daha hızlı, yaklaşık 10x, neden? Başlıklardan anlatabileceğim tek şey,dispatch_once()
ilepthread_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!
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 ... –
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. –
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). –