13

मैं जानना चाहता था कि pthread_once() और sem_wait() या dispatch_ * फ़ंक्शंस जैसे POSIX कॉल का उपयोग करने के लिए बेहतर/तेज़ क्या होगा, इसलिए मैंने थोड़ा परीक्षण किया और परिणामों पर आश्चर्यचकित हूं (प्रश्न और परिणाम अंत में हैं)।प्रदर्शन परीक्षण: sem_t vs.s. dispatch_semaphore_t और pthread_once_t vs.s. dispatch_once_t

परीक्षण कोड में मैं mach_absolute_time() को कॉल के समय का उपयोग कर रहा हूं। मुझे सच में परवाह नहीं है कि यह बिल्कुल नैनो-सेकंड के साथ मेल नहीं खा रहा है; मैं मूल्यों की तुलना एक दूसरे के साथ कर रहा हूं ताकि सटीक समय इकाइयां कोई फर्क नहीं पड़ता, केवल अंतराल के बीच अंतर। परिणाम खंड में संख्या दोहराने योग्य और औसत नहीं है; मैं समय का औसत हो सकता था लेकिन मैं सटीक संख्या की तलाश नहीं कर रहा हूं।

test.m (सरल सांत्वना आवेदन; संकलित करने के लिए आसान):

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

मेरे आईमैक पर (हिमपात तेंदुए सर्वर 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 

मैं:

 
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 

मेरे मैकबुकप्रो (हिम तेंदुए 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 

मुझे मिल गया:

 
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 पर मुझे मिल गया:

 

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 

प्रश्न और बयान:

  • sem_wait() और sem_post() धीमी गति से जब कर रहे हैं विवाद के तहत
    • यह मामला क्यों है?
    • क्या ओएसएक्स संगत एपीआई की परवाह नहीं करता है? क्या कोई विरासत कोड है जो इसे धीमा करने के लिए मजबूर करता है?
    • ये संख्याएं dispatch_semaphore फ़ंक्शंस के समान क्यों नहीं हैं?
  • sem_wait() और sem_post() जब वे नहीं कर रहे हैं (वहाँ एक अंतर है के रूप में जब विवाद के तहत बस के रूप में धीमी गति से कर रहे हैं, लेकिन मुझे लगता है कि यह विवाद के तहत और नहीं में बहुत बड़ा अंतर हो सकता है; मैं उम्मीद क्या में था की तरह संख्या dispatch_semaphore कोड)
  • sem_wait() और sem_post() नामित सेमफोर का उपयोग करते समय धीमे होते हैं।
    • क्यों? ऐसा इसलिए है क्योंकि सेमफोर को प्रक्रियाओं के बीच समन्वयित किया जाना चाहिए? ऐसा करने पर शायद अधिक सामान हो सकता है। जब विवाद में नहीं (कोई आश्चर्य की बात यहाँ के बाद से सेब इस एक बहुत की दलाली कर रहा है)
  • dispatch_semaphore_wait() और dispatch_semaphore_signal() बहुत तेज़ है।
  • dispatch_semaphore_wait() और dispatch_semaphore_signal() जब विवाद
    • के तहत धीमी गति से sem_wait() और sem_post() 3 गुना कर रहे हैं ऐसा क्यों इतनी धीमी गति से है? यह मुझे समझ में नहीं आता है। मैं उम्मीद करता हूं कि यह विवाद के तहत sem_t के बराबर होगा।
  • dispatch_once()pthread_once() से लगभग 10x, तेज क्यों है? हेडर से एकमात्र चीज जो मैं बता सकता हूं वह यह है कि के साथ pthread_once() के साथ कोई फ़ंक्शन कॉल बोझ नहीं है।

प्रेरणा: मैं उपकरण काम सेमाफोर के लिए करवाने के लिए के 2 सेट के साथ प्रस्तुत कर रहा हूँ या एक बार कॉल (मैं वास्तव में इस बीच में अन्य सेमाफोर वेरिएंट पाया, लेकिन जब तक एक के रूप में लाया मैं उन पर ध्यान नहीं देगा बेहतर विकल्प)। मैं सिर्फ यह जानना चाहता हूं कि नौकरी के लिए सबसे अच्छा उपकरण क्या है (यदि आपके पास फिलिप्स या फ्लैटहेड के साथ स्क्रू में स्क्रू करने का विकल्प है, तो मुझे फिलिप्स चुनना होगा यदि मुझे स्क्रू और फ्लैटहेड को टॉर्क करना नहीं है तो मुझे पेंच टोक़)। ऐसा लगता है कि अगर मैं libdispatch साथ उपयोगिताओं लेखन शुरू मैं अन्य ऑपरेटिंग सिस्टम है कि libdispatch अभी तक काम कर की जरूरत नहीं है करने के लिए बंदरगाह उन्हें करने में सक्षम नहीं हो सकता है ... लेकिन यह इतना उपयोग करने के लिए मोहक है;)

यह खड़ा के रूप में: जब मैं पोर्टेबिलिटी और POSIX कॉल करता हूं तो मुझे libdispatch का उपयोग करना होगा जब मैं करता हूं।

धन्यवाद!

उत्तर

10

sem_wait() और sem_post() भारी वजन सिंक्रनाइज़ेशन सुविधाएं हैं जिनका उपयोग प्रक्रियाओं के बीच किया जा सकता है। वे हमेशा कर्नेल को राउंड ट्रिप शामिल करते हैं, और शायद हमेशा आपके थ्रेड को पुन: निर्धारित करने की आवश्यकता होती है। वे आमतौर पर इन-प्रोसेस सिंक्रनाइज़ेशन के लिए सही विकल्प नहीं हैं। मुझे यकीन है कि क्यों नामित वेरिएंट गुमनाम लोगों की तुलना में धीमी हो जाएगा नहीं कर रहा हूँ ...

मैक ओएस एक्स वास्तव में Posix संगतता के बारे में बहुत अच्छी है ... लेकिन Posix विनिर्देशों वैकल्पिक कार्यों का एक बहुत कुछ है, और मैक उनके पास सब नहीं है। आपकी पोस्ट वास्तव में पहली बार मैंने pthread_barriers के बारे में सुना है, इसलिए मुझे लगता है कि वे अपेक्षाकृत हाल ही में हैं, या सभी सामान्य नहीं हैं। (मैंने पिछले दस वर्षों या उससे भी अधिक के लिए pthreads विकास पर अधिक ध्यान नहीं दिया है।)

प्रेषण सामग्री मजबूर चरम विवाद के तहत अलग हो जाने का कारण शायद कवर के तहत व्यवहार स्पिन ताले के समान है। आपके प्रेषण कार्यकर्ता धागे आशावादी धारणा के तहत अपने क्वांटा का एक अच्छा हिस्सा बर्बाद कर रहे हैं कि विवाद के तहत संसाधन अब किसी भी चक्र उपलब्ध होगा ... शार्क के साथ थोड़ी देर आपको निश्चित रूप से बताएगी। हालांकि, ले-होम प्वाइंट होना चाहिए कि विवाद के दौरान थ्रैशिंग को "अनुकूलित करना" प्रोग्रामर समय का खराब निवेश है। इसके बजाय कोड को पर अनुकूलित करने का समय पहले स्थान पर भारी विवाद से बचें।

यदि आपके पास वास्तव में एक संसाधन है जो आपकी प्रक्रिया के भीतर एक गैर-टालने योग्य बाधा है, तो इसके आस-पास एक सेमफोर डालने से बड़े पैमाने पर उप-इष्टतम होता है। इसे अपने स्वयं के सीरियल प्रेषण कतार पर रखें, और जितना संभव हो उतना डिस्पैच_एसिंक ब्लॉक उस कतार पर निष्पादित किए जाएंगे।

अंत में, dispatch_once() pthread_once() से तेज़ है क्योंकि यह spec'd और वर्तमान प्रोसेसर पर तेज़ होने के लिए लागू किया गया है। शायद ऐप्पल pthread_once() कार्यान्वयन को तेज कर सकता है, क्योंकि मुझे संदेह है कि संदर्भ कार्यान्वयन pthread सिंक्रनाइज़ेशन प्राइमेटिव का उपयोग करता है, लेकिन ... अच्छा ... उन्होंने इसके बजाय सभी libdispatch goodness प्रदान की है।:-)

+2

sem_wait/post के बारे में अच्छा बिंदु dispatch_semaphores को कर्नेल को संदर्भ स्विच से निपटने की ज़रूरत नहीं है (यह एक डुह की तरह लगता है!); मैं एम्बेडेड सिस्टम के लिए घर उगाए गए कर्नेल को पॉज़िक्स संगतता जोड़ने के लिए ज़िम्मेदार था और यूनिट परीक्षण बनाने के लिए बाधाओं को उपयोगी पाया है। मैं एक विशेष स्थिति को दूसरे पर अनुकूलित करने की कोशिश नहीं कर रहा था, बल्कि मुझे दिए गए 2 टूल्स से बाहर निकलने का प्रयास करें जो नौकरी के लिए सबसे अच्छा उपकरण है (यदि मेरे पास फिलिप्स या फ्लैटहेड के साथ स्क्रू में स्क्रूइंग का विकल्प है। .. मैं फिलिप्स का उपयोग करूंगा)। प्रेरणा के साथ प्रश्न अपडेट कर रहा है ... –

+0

pthread_once() को "कॉल" चेक के लिए परमाणुओं के साथ कार्यान्वित किया जा सकता है; हाँ को पूरा करने के लिए कॉल का इंतजार करते समय मैं सहमत हूं कि यह अन्य धागे को अवरुद्ध करने के लिए pthread_mutex का उपयोग करेगा (इस तरह मैंने इसे किया)। हालांकि इस परीक्षण मामले में, कोई अवरोधन नहीं है और कर्नेल संदर्भ स्विच की कोई आवश्यकता नहीं है। इसके साथ में मैंने अभी भी समझ में नहीं आता कि 10x अंतर क्यों है। मुझे लगता है कि libdispatch posix कॉल से अधिक अनुकूलित है। –

+0

एक और अवलोकन: अज्ञात सेमफोरों को dispatch_semaphores की तरह काम करना चाहिए क्योंकि आप उन्हें किसी अन्य प्रक्रिया से पुनर्प्राप्त नहीं कर सकते हैं। आपको केवल कर्नेल पर स्विच करना होगा जब आपको वास्तव में अवरुद्ध धागे को अवरुद्ध करना या जागृत करना होगा (यह देखते हुए कि सेब सैमफोरों के लिए परमाणुओं का उपयोग कर रहा है, जो हमने अपने ओएस में हमारे सेफफोर्स के लिए किया है)। –

संबंधित मुद्दे