سؤال

أردت أن أعرف ما يمكن أن يكون أفضل/أسرع إلى استخدام مكالمات مثل POSIX pthread_once() و sem_wait() أو dispatch_* وظائف, لذلك أنا خلقت اختبار قليلا و مندهش من النتائج (أسئلة النتائج في النهاية).

في اختبار التعليمات البرمجية أنا باستخدام mach_absolute_time() إلى وقت المكالمات.أنا حقا لا أهتم أن هذا ليس بالضبط مطابقة مع نانو ثانية ؛ أنا مقارنة القيم مع بعضها البعض حتى في الوقت المحدد الوحدات لا يهم, فقط الاختلافات بين الفاصل القيام به.الأرقام في قسم النتائج للتكرار وليس متوسط;أنا يمكن أن يكون متوسط الزمن ولكن أنا لا أبحث عن الأرقام الدقيقة.

الاختبار.م (وحدة التحكم بسيطة التطبيق ؛ من السهل تجميع):

#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

على 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

علي:

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

على اي فون 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() بطيئة عندما لا تكون قيد الخلاف
    • لماذا هذه القضية ؟
    • لا OSX لا يهتمون متوافق مع واجهات برمجة التطبيقات ؟ هناك بعض التعليمات البرمجية القديمة أن هذه القوات أن تكون بطيئة ؟
    • لماذا هذه الأرقام نفس dispatch_semaphore الوظائف ؟
  • sem_wait() و sem_post() هي مجرد بطيئة عند الخلاف عندما كانوا لا (هناك فرق ولكن أعتقد أنه سيكون هناك فرق كبير بين رعاياها و لا ؛ توقعت أرقام مثل ما كان في dispatch_semaphore رمز)
  • sem_wait() و sem_post() هي أبطأ عند استخدام اسمه الإشارات.
    • لماذا ؟ هذا لأن الإشارة قد تكون مزامنة بين العمليات ؟ ربما هناك المزيد من الأمتعة عندما تفعل ذلك.
  • dispatch_semaphore_wait() و dispatch_semaphore_signal() مجانين بسرعة عندما لا تكون تحت ادعاء (لا مفاجأة هنا منذ أبل هو يروج هذا الكثير).
  • dispatch_semaphore_wait() و dispatch_semaphore_signal() هي 3x أبطأ من sem_wait() و sem_post() عند الخلاف
    • لماذا هذا بطيئة جدا ؟ هذا لا معنى له بالنسبة لي.هذا متوقع أن تكون على قدم المساواة مع sem_t تحت الخلاف.
  • dispatch_once() هو أسرع من pthread_once(), حوالي 10 لماذا ؟ الشيء الوحيد الذي أستطيع أن أقول من رؤوس أنه لا يوجد استدعاء دالة العبء مع dispatch_once() من pthread_once().

الدافع: أنا قدمت مع 2 مجموعات من الأدوات المهمة على الإشارات أو مرة المكالمات (أنا فعلا وجدت إشارة أخرى المتغيرات في غضون ذلك, ولكن سوف تجاهل تلك إلا بوصفه الخيار الأفضل).أنا فقط أريد أن أعرف ما هي أفضل وسيلة للحصول على الوظيفة (إذا كان لديك الخيار الشد في المسمار مع فيليبس أو مسطح الرأس ، وأود أن اختيار فيليبس إذا لم يكن لديك عزم دوران المسمار الرأس إذا كان لدي عزم دوران المسمار).ويبدو أنه إذا كنت بدء الكتابة المرافق مع libdispatch أنا قد لا تكون قادرة على منفذ لهم إلى أنظمة التشغيل الأخرى التي لا تملك libdispatch تعمل حتى الآن...وإنما هو مغرية جدا للاستخدام؛)

كما هو عليه:وسوف تستخدم libdispatch عندما كنت لا داعي للقلق حول قابلية POSIX المكالمات عندما أفعل.

وذلك بفضل!

هل كانت مفيدة؟

المحلول

sem_wait() و sem_post() ثقيلة الوزن تزامن المرافق التي يمكن استخدامها بين العمليات.أنها تنطوي دائما رحلات إلى النواة ، وربما تتطلب دائما الخيط الخاص بك لتحديد موعد جديد.وعادة لا يكون له الحق في الاختيار في عملية المزامنة.أنا لست متأكدا من السبب في تسمية المتغيرات سيكون أبطأ من المجهول منها...

نظام التشغيل Mac OS X هو في الواقع جيدة جدا حول Posix التوافق...ولكن Posix المواصفات لدينا الكثير من وظائف اختيارية و ماك لا يكون لهم كل شيء.وظيفة الخاص بك هو في الواقع أول من أي وقت مضى لقد سمعت من pthread_barriers, لذا أعتقد أنهم إما حديثة نسبيا ، أو ليس كل هذا المشتركة.(لم تدفع الكثير من الاهتمام pthreads تطور على مدى السنوات العشر الماضية أو نحو ذلك.)

سبب إرسال الأشياء تنهار تحت القسري المتطرفة الخلاف هو الأرجح لأن تحت الأغطية سلوك مشابه تدور في الأقفال.الإرسال الخاصة بك الترابط العامل المرجح جدا أن تضيع جزءا كبيرا من الكميات تحت متفائل افتراض أن الموارد في إطار التنافس سوف تكون متاحة في أي دورة الآن...قليلا من الوقت مع القرش سوف اقول لكم لعلى يقين.اتخاذ المنزل نقطة ، رغم ذلك ، ينبغي أن "تحسين" في سحق أثناء الخلاف هو سوء استثمار مبرمج الوقت.بدلا من قضاء الوقت في تحسين التعليمات البرمجية تجنب الثقيلة الخلاف في المقام الأول.

إذا كان لديك حقا مورد الامم المتحدة يمكن تجنبها عنق الزجاجة داخل عملية وضع إشارة حوله بشكل كبير دون المستوى الأمثل.وضعت على المسلسل ارسال قائمة الانتظار ، قدر الإمكان dispatch_async كتل ليتم تنفيذها على قائمة الانتظار.

وأخيرا ، dispatch_once() هو أسرع من pthread_once() لأنه spec'd وتنفيذ أن تكون سريعة على المعالجات الحالية.ربما أبل يمكن أن تصل سرعة pthread_once () ، وأظن الإشارة تنفيذ يستخدم pthread تزامن الأوليات ، ولكن...حسنا...لقد قدمت كل من libdispatch الخير بدلا من ذلك.:-)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top