c - ويندوز - حل مشكلة وحدة المعالجة المركزية




كيفية الحصول على استخدام CPU بنسبة 100٪ من برنامج C (6)

هذا سؤال مثير للاهتمام ، لذا دعوني أضع المشهد. أعمل في المتحف الوطني للحوسبة ، وقد نجحنا للتو في الحصول على كمبيوتر Cray Y-MP EL super من عام 1992 ، ونريد أن نرى مدى السرعة التي يمكن أن يسير بها!

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

سرعان ما توصلنا إلى هذا الرمز لحساب الأعداد الأولية:

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

void main() {
    clock_t start, end;
    double runTime;
    start = clock();
    int i, num = 1, primes = 0;

    while (num <= 1000) { 
        i = 2; 
        while (i <= num) { 
            if(num % i == 0)
                break;
            i++; 
        }
        if (i == num)
            primes++;

        system("clear");
        printf("%d prime numbers calculated\n",primes);
        num++;
    }

    end = clock();
    runTime = (end - start) / (double) CLOCKS_PER_SEC;
    printf("This machine calculated all %d prime numbers under 1000 in %g seconds\n", primes, runTime);
}

الذي يعمل على الكمبيوتر المحمول ثنائي النواة الذي تديره Ubuntu (The Cray يمتد UNICOS) ، يعمل بشكل مثالي ، ويحصل على استخدام CPU بنسبة 100٪ ويستغرق حوالي 10 دقائق أو نحو ذلك. عندما وصلت إلى المنزل ، قررت أن أجربها على جهاز ألعاب الكمبيوتر الحديث ، وهذا هو المكان الذي نحصل فيه على أول مشاكلنا.

قمت أولاً بتعديل الشفرة للتشغيل على Windows لأن هذا هو ما كان يستخدمه الكمبيوتر الشخصي للألعاب ، ولكن كان حزنًا لإيجاد أن العملية كانت تحصل على حوالي 15٪ فقط من طاقة وحدة المعالجة المركزية. فكنت أحسب أنه يجب أن يكون Windows هو Windows ، لذا قمت بالتمهيد إلى قرص مضغوط مباشر لـ Ubuntu معتقدًا أن Ubuntu ستسمح بتشغيل العملية بإمكانياتها الكاملة كما كانت تفعل في وقت سابق على الكمبيوتر المحمول الخاص بي.

ومع ذلك ، لدي فقط 5 ٪ من الاستخدام! إذن ، سؤالي هو ، كيف يمكنني تكييف البرنامج ليعمل على جهاز الألعاب الخاص بي في Windows 7 أو Live Linux باستخدام 100٪ من وحدة المعالجة المركزية؟ شيء آخر من شأنه أن يكون كبيرا ولكن ليس من الضروري هو إذا كان المنتج النهائي يمكن أن يكون واحد .exe التي يمكن توزيعها بسهولة وتشغيلها على أجهزة ويندوز.

شكرا جزيلا!

PS بالطبع لم يعمل هذا البرنامج بالفعل مع معالجات Crays 8 المتخصصة ، وهذه مشكلة أخرى تمامًا ... إذا كنت تعرف أي شيء حول تحسين الرمز للعمل على أجهزة كمبيوتر Cray super 90 تعطينا صيحة أيضًا!


نحن حقا نريد أن نرى كيف يمكن أن تذهب بسرعة!

خوارزمية الخاص بك لإنشاء أرقام الأولية غير فعالة للغاية. قارنها مع primegen الذي يولد 50847534 الأولية حتى 1000000000 في 8 ثوان فقط على Pentium II-350.

لاستهلاك جميع وحدات المعالجة المركزية بسهولة ، يمكنك حل مشكلة موازية محرجة ، على سبيل المثال ، حساب Mandelbrot وضع أو استخدام البرمجة الجينية لرسم Mona Lisa في مواضيع متعددة (العمليات).

نهج آخر هو أن تأخذ برنامجا قياسيا حاسما للحاسوب العملاق Cray وتقوم بتوصيله إلى جهاز كمبيوتر حديث.


TLDR. الإجابة المقبولة هي غير فعالة وغير متوافقة. بعد الألكاو يعمل 100X أسرع.

لا يمكن تشغيل مترجم gcc المتوفرة على MAC omp . اضطررت لتثبيت llvm (brew install llvm ) . لكنني لم أرى وحدة المعالجة المركزية الخمول يسير أثناء تشغيل إصدار OMP.

هنا لقطة شاشة أثناء تشغيل إصدار OMP.

بدلاً من ذلك ، استخدمت مؤشر POSIX الأساسي ، الذي يمكن تشغيله باستخدام أي مترجم c وشاهد وحدة المعالجة المركزية بالكامل تقريبًا المستخدمة عند nos of thread = no of cores = 4 (MacBook Pro، 2.3 GHz Intel Core i5). هنا البرنامج -

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define NUM_THREADS     10
#define THREAD_LOAD 100000
using namespace std;

struct prime_range {
    int min;
    int max;
    int total;
};

void* findPrime(void *threadarg)
{
    int i, primes = 0;
    struct prime_range *this_range;
    this_range = (struct prime_range *) threadarg;

    int minLimit =  this_range -> min ;
    int maxLimit =  this_range -> max ;
    int flag = false;
    while (minLimit <= maxLimit) {
        i = 2;
        int lim = ceil(sqrt(minLimit));
        while (i <= lim) {
            if (minLimit % i == 0){
                flag = true;
                break;
            }
            i++;
        }
        if (!flag){
            primes++;
        }
        flag = false;
        minLimit++;
    }
    this_range ->total = primes;
    pthread_exit(NULL);
}

int main (int argc, char *argv[])
{
    struct timespec start, finish;
    double elapsed;

    clock_gettime(CLOCK_MONOTONIC, &start);

    pthread_t threads[NUM_THREADS];
    struct prime_range pr[NUM_THREADS];
    int rc;
    pthread_attr_t attr;
    void *status;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    for(int t=1; t<= NUM_THREADS; t++){
        pr[t].min = (t-1) * THREAD_LOAD + 1;
        pr[t].max = t*THREAD_LOAD;
        rc = pthread_create(&threads[t], NULL, findPrime,(void *)&pr[t]);
        if (rc){
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }
    int totalPrimesFound = 0;
    // free attribute and wait for the other threads
    pthread_attr_destroy(&attr);
    for(int t=1; t<= NUM_THREADS; t++){
        rc = pthread_join(threads[t], &status);
        if (rc) {
            printf("Error:unable to join, %d" ,rc);
            exit(-1);
        }
        totalPrimesFound += pr[t].total;
    }
    clock_gettime(CLOCK_MONOTONIC, &finish);
    elapsed = (finish.tv_sec - start.tv_sec);
    elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
    printf("This machine calculated all %d prime numbers under %d in %lf seconds\n",totalPrimesFound, NUM_THREADS*THREAD_LOAD, elapsed);
    pthread_exit(NULL);
}

لاحظ كيف يتم استخدام وحدة المعالجة المركزية بالكامل -

ملاحظة - إذا قمت بزيادة عدد مؤشرات الترابط ، فإن استخدام وحدة المعالجة المركزية الفعلي يتراجع (حاول إجراء no of threads = 20.) نظرًا لأن النظام يستخدم المزيد من الوقت في تبديل السياق من الحوسبة الفعلية.

بالمناسبة ، لم يكن الجهاز الخاص بي سمين كماmystical (الإجابة المقبولة). ولكن رصيدي مع الترابط POSIX الأساسية يعمل بطريقة أسرع من OMP واحد. ها هي النتيجة -

PS زيادة حمولة الترابط إلى 2.5 مليون لرؤية استخدام وحدة المعالجة المركزية ، لأنه يكتمل في أقل من ثانية.


إذا كنت تريد وحدة المعالجة المركزية بنسبة 100 ٪ ، تحتاج إلى استخدام أكثر من 1 الأساسية. للقيام بذلك ، تحتاج إلى عدة خيوط.

فيما يلي إصدار متوازي باستخدام OpenMP:

واضطررت إلى زيادة الحد إلى 1000000 لجعله يستغرق أكثر من ثانية واحدة على جهازي.

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

int main() {
    double start, end;
    double runTime;
    start = omp_get_wtime();
    int num = 1,primes = 0;

    int limit = 1000000;

#pragma omp parallel for schedule(dynamic) reduction(+ : primes)
    for (num = 1; num <= limit; num++) { 
        int i = 2; 
        while(i <= num) { 
            if(num % i == 0)
                break;
            i++; 
        }
        if(i == num)
            primes++;
//      printf("%d prime numbers calculated\n",primes);
    }

    end = omp_get_wtime();
    runTime = end - start;
    printf("This machine calculated all %d prime numbers under %d in %g seconds\n",primes,limit,runTime);

    return 0;
}

انتاج:

هذه الآلة تحسب جميع الأعداد الأولية 78498 تحت 1000000 في 29.753 ثانية

إليك وحدة المعالجة المركزية بنسبة 100٪:


السبب الذي يجعلك تحصل على 15٪ من المعالج الأساسي هو أن كودك يستخدم 1 core بنسبة 100٪. 100/6 = 16.67٪ ، والتي باستخدام المتوسط ​​المتحرك مع جدولة العملية (يمكن تشغيل العملية الخاصة بك تحت الأولوية العادية) يمكن أن يتم تسجيلها بسهولة بنسبة 15٪.

ولذلك ، من أجل استخدام وحدة المعالجة المركزية 100 ٪ ، سوف تحتاج إلى استخدام جميع النوى في وحدة المعالجة المركزية الخاصة بك - إطلاق مسارات رمز تنفيذ متوازي 6 لوحدة المعالجة المركزية الأساسية سداسية ويكون هذا النطاق يصل إلى مع ذلك العديد من معالجات الجهاز الخاص بك Cray :)


كن على دراية أيضًا بكيفية تحميل وحدة المعالجة المركزية. يمكن لوحدة المعالجة المركزية القيام بالكثير من المهام المختلفة ، وفي حين سيتم الإبلاغ عن الكثير منها على أنها "تحميل وحدة المعالجة المركزية 100٪" ، فقد تستخدم كل منها 100٪ من الأجزاء المختلفة من وحدة المعالجة المركزية. بمعنى آخر ، من الصعب جدًا مقارنة وحدتين مختلفتين من وحدات المعالجة المركزية (CPUs) للأداء ، وخصوصًا معماري CPU مختلفين. تنفيذ المهمة أ قد تفضل وحدة معالجة مركزية واحدة على أخرى ، في حين أن تنفيذ المهمة ب يمكن بسهولة أن يكون العكس (بما أن وحدتي وحدة المعالجة المركزية قد يكون لها موارد مختلفة داخليًا وقد تقوم بتنفيذ تعليمات برمجية مختلفة تمامًا).

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

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

لذا يجب أن يكون السؤال الأول: ما هي معلمة الأداء التي تهمك؟ ما الذي تريد قياسه؟ إذا كنت ترغب في معرفة الجهاز الذي يحصل على أكبر عدد من FPS من Quake 4 ، فإن الإجابة سهلة. جهاز الألعاب الخاص بك ، كما لا يستطيع Cray تشغيل هذا البرنامج على الإطلاق ؛-)

ابتهاج ، ستين


للحصول على تحسين سريع على أحد النواة ، أزل مكالمات النظام للحد من تبديل السياق. أزل هذه السطور:

system("clear");
printf("%d prime numbers calculated\n",primes);

الأول سيء بشكل خاص ، لأنه سيولد عملية جديدة كل عملية تكرار.







cray