لماذا لا يظهر جهاز الكمبيوتر الخاص بي تسريعا عند استخدام الرمز الموازي؟

StackOverflow https://stackoverflow.com/questions/2405461

سؤال

لذلك أدرك أن هذا السؤال يبدو غبيا (ونعم أنا أستخدم نواة مزدوجة)، لكنني جربت اثنين من المكتبات المختلفة (الإرسال الكبير المركزي و OpenMP)، وعند استخدام الساعة () إلى وقت التعليمات البرمجية مع وبدون الخطوط التي تصنعها متوازي، والسرعة هي نفسها. (للسجل كانوا يستخدمون شكلهم الموازي الخاص بهم). أنها تقارير يجري تشغيلها على خيوط مختلفة، ولكن ربما يركضون في نفس النواة؟ هل هناك أي طريقة للتحقق؟ (كلا المكتبات مخصصة ل C، أنا غير مرتاح في طبقات أقل.) هذا غريب للغاية. أيه أفكار؟

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

المحلول

تحرير: مزيد من التفاصيل للإرسال الكبير المركزي استجابة للتعليق المرجع.

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

إنه فقط أن المهمة منقولة بين الخيوط، لذلك الوقت الكلي الذي يجب أن تنتظره أقل. يجب أن تستخدم وقت الحائط (الوقت على ساعة الحائط). يوفر OpenMP روتين omp_get_wtime() للقيام بذلك. خذ الروتين التالي كمثال:

#include <omp.h>
#include <time.h>
#include <math.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    int i, nthreads;
    clock_t clock_timer;
    double wall_timer;
    for (nthreads = 1; nthreads <=8; nthreads++) {
        clock_timer = clock();
        wall_timer = omp_get_wtime();
        #pragma omp parallel for private(i) num_threads(nthreads)
        for (i = 0; i < 100000000; i++) cos(i);
        printf("%d threads: time on clock() = %.3f, on wall = %.3f\n", \
            nthreads, \
            (double) (clock() - clock_timer) / CLOCKS_PER_SEC, \
            omp_get_wtime() - wall_timer);
    }
}

النتائج هي:

1 threads: time on clock() = 0.258, on wall = 0.258
2 threads: time on clock() = 0.256, on wall = 0.129
3 threads: time on clock() = 0.255, on wall = 0.086
4 threads: time on clock() = 0.257, on wall = 0.065
5 threads: time on clock() = 0.255, on wall = 0.051
6 threads: time on clock() = 0.257, on wall = 0.044
7 threads: time on clock() = 0.255, on wall = 0.037
8 threads: time on clock() = 0.256, on wall = 0.033

يمكنك أن ترى أن clock() الوقت لا يتغير كثيرا. أحصل على 0.254 بدون pragma, ، لذلك فهي أبطأ قليلا باستخدام OpenMP مع موضوع واحد عن عدم استخدام OpenMP على الإطلاق، لكن وقت الجدار ينقص مع كل مؤشر ترابط.

لن يكون التحسن دائما جيدا بسبب، على سبيل المثال، أجزاء من حسابك غير مواز (انظر amdahl's_law.) أو خيوط مختلفة القتال على نفس الذاكرة.

تحرير: للحصول على الإرسال الكبير المركزي، مرجع GCD. الدول، التي يستخدمها GCD gettimeofday لحالة الجدار. لذلك، أقوم بإنشاء تطبيق Cocoa جديد، وفي applicationDidFinishLaunching وضعت:

struct timeval t1,t2;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int iterations = 1; iterations <= 8; iterations++) {
    int stride = 1e8/iterations;
    gettimeofday(&t1,0);
    dispatch_apply(iterations, queue, ^(size_t i) { 
        for (int j = 0; j < stride; j++) cos(j); 
    });
    gettimeofday(&t2,0);
    NSLog(@"%d iterations: on wall = %.3f\n",iterations, \
                t2.tv_sec+t2.tv_usec/1e6-(t1.tv_sec+t1.tv_usec/1e6));
}

وأحصل على النتائج التالية على وحدة التحكم:

2010-03-10 17:33:43.022 GCDClock[39741:a0f] 1 iterations: on wall = 0.254
2010-03-10 17:33:43.151 GCDClock[39741:a0f] 2 iterations: on wall = 0.127
2010-03-10 17:33:43.236 GCDClock[39741:a0f] 3 iterations: on wall = 0.085
2010-03-10 17:33:43.301 GCDClock[39741:a0f] 4 iterations: on wall = 0.064
2010-03-10 17:33:43.352 GCDClock[39741:a0f] 5 iterations: on wall = 0.051
2010-03-10 17:33:43.395 GCDClock[39741:a0f] 6 iterations: on wall = 0.043
2010-03-10 17:33:43.433 GCDClock[39741:a0f] 7 iterations: on wall = 0.038
2010-03-10 17:33:43.468 GCDClock[39741:a0f] 8 iterations: on wall = 0.034

وهو ما يتعلق بنفس الطريقة التي كنت أحصل عليها.

هذا مثال مفاصل للغاية. في الواقع، عليك أن تأكد من الاحتفاظ بالتحسين في -O0، وإلا فإن المحول البرمجي سيدرك أننا لا نحتفظ بأي من الحسابات وعدم القيام الحلقة على الإطلاق. أيضا، عدد صحيح أنا أخذ cos يختلف في المثالين، ولكن هذا لا يؤثر على النتائج أكثر من اللازم. انظر STRIDE على manepage ل dispatch_apply لكيفية القيام بذلك بشكل صحيح ولهذا iterations قابلة للمقارنة على نطاق واسع num_threads في هذه الحالة.

تحرير: أشير إلى أن إجابة يعقوب تتضمن

يمكنني استخدام وظيفة OMP_GET_TREAD_NUM () ضمن حلقة مكتوبة لطباعة النواة التي تعمل عليها ... بهذه الطريقة يمكنك التأكد من أنها تعمل على كلا النوى.

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

#include <omp.h>
#include <stdio.h>

int main() {
    int i;
    #pragma omp parallel for private(i) num_threads(50)
    for (i = 0; i < 50; i++) printf("%d\n", omp_get_thread_num());
}

يطبع أنه يستخدم الخيوطين من 0 إلى 49، ولكن هذا لا يظهر النواة التي تعمل عليها، حيث أن لدي فقط ثمانية النوى. من خلال النظر إلى شاشة النشاط (المرجع المذكور GCD، لذلك يجب أن يكون على ماك - اذهب Window/CPU Usage)، يمكنك رؤية الوظائف تبديل بين النوى، لذلك الأساسية! = موضوع.

نصائح أخرى

على الأرجح وقت التنفيذ الخاص بك غير مرتبط بتلك الحلقات التي تواجهها.

اقتراحي هو أنك ملف التعريف الخاص بك لمعرفة ما يستغرق معظم الوقت. معظم المهندسين سوف يخبرونك أنه يجب عليك القيام بذلك قبل القيام بأي شيء جاد لتحسين الأشياء.

من الصعب تخمين دون أي تفاصيل. ربما طلبك ليس حتى cpu ملزمة. هل شاهدت تحميل وحدة المعالجة المركزية أثناء تشغيل الكود الخاص بك؟ هل وصلت إلى 100٪ على أساس واحد على الأقل؟

سؤالك يفتقد بعض التفاصيل الحاسمة للغاية مثل ما هي طبيعة طلبك هو، ما هو جزء منه تحاول تحسينه، نتائج التنميط (إن وجدت)، إلخ ...

بعد القول أنه يجب عليك أن تتذكر العديد من النقاط الحرجة عند الاقتراب من جهود تحسين الأداء:

  • يجب أن تركز الجهود دائما على مجالات التعليمات البرمجية التي أثبتت عليها، عن طريق التنميط, أن تكون غير فعالة
  • الموافقة على CPU Bound Code سوف على الاغلب لا تحسين الأداء (على جهاز أساسي واحد). سوف تفقد الوقت الثمين على مفاتيح السياق غير الضرورية واكتسابها لا شيئ. وبعد يمكنك بسهولة جدا أداء تفاقم من خلال القيام بهذا.
  • حتى إذا كنت متوازيا من التعليمات البرمجية المنفصلة وحدة المعالجة المركزية على جهاز متعدد الصور، فيجب أن تتذكر أنك لا تواجه أي ضمانات للتنفيذ الموازي.

تأكد من أنك لن تعارض هذه النقاط، لأن تخمين تعليمي (يحظر أي تفاصيل إضافية) سيقول هذا هو بالضبط ما تفعله.

إذا كنت تستخدم الكثير من الذاكرة داخل الحلقة، فقد يمنع ذلك من كونه أسرع. كما يمكنك أن تنظر إلى مكتبة Pthread، لتتعامل مع الخيوط يدويا.

انا استعمل ال omp_get_thread_num() دالة داخل حلقة بلدي المتوازية لطباعة النواة التي تعمل عليها إذا كنت لا تحدد num_threads. وبعد على سبيل المثال،

printf("Computing bla %d on core %d/%d ...\n",i+1,omp_get_thread_num()+1,omp_get_max_threads());

ما سبق سيعمل على هذا pragma #pragma OMP الموازي مقابل الافتراضي (لا شيء) مشترك (A، B، C)

وبهذه الطريقة، يمكنك التأكد من أنها تعمل على كلا النوى حيث سيتم إنشاء مواضيع 2 فقط.

راجع للشغل، هل تم تمكين OpenMP عند تجميعه؟ في Visual Studio، يجب عليك تمكينه في صفحات الملكية, C++ -> Language وحدد OpenMP Support ل Yes

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