c للايفون حجم ذاكرة التخزين المؤقت تقدير على النظام الخاص بك؟




مسح الذاكرة المؤقته للاندرويد (2)

حصلت على هذا البرنامج من هذا الرابط ( https://gist.github.com/jiewmeng/3787223).I لقد تم البحث على شبكة الإنترنت مع فكرة الحصول على فهم أفضل من مخابئ المعالج (L1 و L2) .أريد أن أكون قادرة على كتابة برنامج من شأنها أن تمكنني من تخمين حجم L1 و L2 مخبأ على جهاز الكمبيوتر المحمول الجديد. (فقط لغرض التعلم.أنا أعرف أنني يمكن أن تحقق المواصفات.)

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

#define KB 1024
#define MB 1024 * 1024

int main() {
    unsigned int steps = 256 * 1024 * 1024;
    static int arr[4 * 1024 * 1024];
    int lengthMod;
    unsigned int i;
    double timeTaken;
    clock_t start;
    int sizes[] = {
        1 * KB, 4 * KB, 8 * KB, 16 * KB, 32 * KB, 64 * KB, 128 * KB, 256 * KB,
        512 * KB, 1 * MB, 1.5 * MB, 2 * MB, 2.5 * MB, 3 * MB, 3.5 * MB, 4 * MB
    };
    int results[sizeof(sizes)/sizeof(int)];
    int s;

    /*for each size to test for ... */
    for (s = 0; s < sizeof(sizes)/sizeof(int); s++)
    {
            lengthMod = sizes[s] - 1;
            start = clock();
            for (i = 0; i < steps; i++)
            {
                arr[(i * 16) & lengthMod] *= 10;
                arr[(i * 16) & lengthMod] /= 10;
            }

            timeTaken = (double)(clock() - start)/CLOCKS_PER_SEC;
            printf("%d, %.8f \n", sizes[s] / 1024, timeTaken);
    }

    return 0;
}

إخراج البرنامج في الجهاز هو كما يلي.كيف يمكنني تفسير الأرقام؟ ماذا يقول لي هذا البرنامج؟

1, 1.07000000 
4, 1.04000000 
8, 1.06000000 
16, 1.13000000 
32, 1.14000000 
64, 1.17000000 
128, 1.20000000 
256, 1.21000000 
512, 1.19000000 
1024, 1.23000000 
1536, 1.23000000 
2048, 1.46000000 
2560, 1.21000000 
3072, 1.45000000 
3584, 1.47000000 
4096, 1.94000000 

Answer #1
  1. تحتاج الوصول المباشر إلى الذاكرة

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

    rep + movsb,movsw,movsd 
    rep + stosb,stosw,stosd

    لذلك يمكنك قياس نقل الذاكرة وليس شيئا آخر مثل في التعليمات البرمجية الخاصة بك!

  2. وقياس أوقات النقل الخام ورسم رسم بياني

    • x المحور هو نقل كتلة الحجم
    • المحور y هو سرعة نقل

    المناطق التي لها نفس معدل النقل تتفق مع طبقة كاش المناسبة

[Edit1] لا يمكن العثور على بلدي شفرة المصدر القديمة لهذا حتى ضبطت شيئا الآن في C ++ للنوافذ :

قياس الوقت:

//---------------------------------------------------------------------------
double performance_Tms=-1.0,    // perioda citaca [ms]
       performance_tms= 0.0;    // zmerany cas [ms]
//---------------------------------------------------------------------------
void tbeg()
    {
    LARGE_INTEGER i;
    if (performance_Tms<=0.0) { QueryPerformanceFrequency(&i); performance_Tms=1000.0/double(i.QuadPart); }
    QueryPerformanceCounter(&i); performance_tms=double(i.QuadPart);
    }
//---------------------------------------------------------------------------
double tend()
    {
    LARGE_INTEGER i;
    QueryPerformanceCounter(&i); performance_tms=double(i.QuadPart)-performance_tms; performance_tms*=performance_Tms;
    return performance_tms;
    }
//---------------------------------------------------------------------------

المعيار (32bit التطبيق):

//---------------------------------------------------------------------------
DWORD sizes[]=                  // used transfer block sizes
    {
      1<<10,  2<<10,  3<<10,  4<<10,  5<<10,  6<<10,  7<<10,  8<<10,  9<<10,
     10<<10, 11<<10, 12<<10, 13<<10, 14<<10, 15<<10, 16<<10, 17<<10, 18<<10,
     19<<10, 20<<10, 21<<10, 22<<10, 23<<10, 24<<10, 25<<10, 26<<10, 27<<10,
     28<<10, 29<<10, 30<<10, 31<<10, 32<<10, 48<<10, 64<<10, 80<<10, 96<<10,
    112<<10,128<<10,192<<10,256<<10,320<<10,384<<10,448<<10,512<<10,  1<<20,
      2<<20,  3<<20,  4<<20,  5<<20,  6<<20,  7<<20,  8<<20,  9<<20, 10<<20,
     11<<20, 12<<20, 13<<20, 14<<20, 15<<20, 16<<20, 17<<20, 18<<20, 19<<20,
     20<<20, 21<<20, 22<<20, 23<<20, 24<<20, 25<<20, 26<<20, 27<<20, 28<<20,
     29<<20, 30<<20, 31<<20, 32<<20,
    };
const int N=sizeof(sizes)>>2;   // number of used sizes
double pmovsd[N];               // measured transfer rate rep MOVSD [MB/sec]
double pstosd[N];               // measured transfer rate rep STOSD [MB/sec]
//---------------------------------------------------------------------------
void measure()
    {
    int i;
    BYTE *dat;                              // pointer to used memory
    DWORD adr,siz,num;                      // local variables for asm
    double t,t0;
    HANDLE hnd;                             // process handle

    // enable priority change (huge difference)
    #define measure_priority

    // enable critical sections (no difference)
//  #define measure_lock

    for (i=0;i<N;i++) pmovsd[i]=0.0;
    for (i=0;i<N;i++) pstosd[i]=0.0;
    dat=new BYTE[sizes[N-1]+4];             // last DWORD +4 Bytes (should be 3 but i like 4 more)
    if (dat==NULL) return;
    #ifdef measure_priority
    hnd=GetCurrentProcess(); if (hnd!=NULL) { SetPriorityClass(hnd,REALTIME_PRIORITY_CLASS); CloseHandle(hnd); }
    Sleep(200);                             // wait to change take effect
    #endif
    #ifdef measure_lock
    CRITICAL_SECTION lock;                  // lock handle
    InitializeCriticalSectionAndSpinCount(&lock,0x00000400);
    EnterCriticalSection(&lock);
    #endif
    adr=(DWORD)(dat);
    for (i=0;i<N;i++)
        {
        siz=sizes[i];                       // siz = actual block size
        num=(8<<20)/siz;                    // compute n (times to repeat the measurement)
        if (num<4) num=4;
        siz>>=2;                            // size / 4 because of 32bit transfer
        // measure overhead
        tbeg();                             // start time meassurement
        asm {
            push esi
            push edi
            push ecx
            push ebx
            push eax
            mov ebx,num
            mov al,0
    loop0:  mov esi,adr
            mov edi,adr
            mov ecx,siz
//          rep movsd                       // es,ds already set by C++
//          rep stosd                       // es already set by C++
            dec ebx
            jnz loop0
            pop eax
            pop ebx
            pop ecx
            pop edi
            pop esi
            }
        t0=tend();                          // stop time meassurement
        // measurement 1
        tbeg();                             // start time meassurement
        asm {
            push esi
            push edi
            push ecx
            push ebx
            push eax
            mov ebx,num
            mov al,0
    loop1:  mov esi,adr
            mov edi,adr
            mov ecx,siz
            rep movsd                       // es,ds already set by C++
//          rep stosd                       // es already set by C++
            dec ebx
            jnz loop1
            pop eax
            pop ebx
            pop ecx
            pop edi
            pop esi
            }
        t=tend();                           // stop time meassurement
        t-=t0; if (t<1e-6) t=1e-6;          // remove overhead and avoid division by zero
        t=double(siz<<2)*double(num)/t;     // Byte/ms
        pmovsd[i]=t/(1.024*1024.0);         // MByte/s
        // measurement 2
        tbeg();                             // start time meassurement
        asm {
            push esi
            push edi
            push ecx
            push ebx
            push eax
            mov ebx,num
            mov al,0
    loop2:  mov esi,adr
            mov edi,adr
            mov ecx,siz
//          rep movsd                       // es,ds already set by C++
            rep stosd                       // es already set by C++
            dec ebx
            jnz loop2
            pop eax
            pop ebx
            pop ecx
            pop edi
            pop esi
            }
        t=tend();                           // stop time meassurement
        t-=t0; if (t<1e-6) t=1e-6;          // remove overhead and avoid division by zero
        t=double(siz<<2)*double(num)/t;     // Byte/ms
        pstosd[i]=t/(1.024*1024.0);         // MByte/s
        }
    #ifdef measure_lock
    LeaveCriticalSection(&lock);
    DeleteCriticalSection(&lock);
    #endif
    #ifdef measure_priority
    hnd=GetCurrentProcess(); if (hnd!=NULL) { SetPriorityClass(hnd,NORMAL_PRIORITY_CLASS); CloseHandle(hnd); }
    #endif
    delete dat;
    }
//---------------------------------------------------------------------------

حيث المصفوفات pmovsd[] و pstosd[] معدلات نقل 32bit قياس [MByte/sec] . يمكنك تكوين التعليمات البرمجية عن طريق استخدام / ريم اثنين يحدد في بداية وظيفة قياس.

الناتج الرسومي:

لتحقيق أقصى قدر من الدقة يمكنك تغيير فئة أولوية العملية إلى الحد الأقصى. حتى إنشاء موضوع قياس مع أولوية قصوى (أحاول ذلك ولكن الشيء فوضى حتى في الواقع) وإضافة قسم حرج لذلك لذلك فإن الاختبار لن يكون دون انقطاع من قبل نظام التشغيل كما في كثير من الأحيان (لا فرق واضح مع وبدون المواضيع). إذا كنت ترغب في استخدام نقل Byte ثم تأخذ في الاعتبار أنه يستخدم فقط 16bit السجلات لذلك تحتاج إلى إضافة حلقة وعنوان التكرارات.

PS.

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

أسم التعليمات RDTSC هو الأفضل لهذا (ولكن حذار معناها تغيرت قليلا مع أبنية جديدة).

إذا لم تكن تحت ويندوز ثم تغيير وظائف tbeg,tend إلى مكافئ نظام التشغيل الخاص بك

[عدل 2] مزيد من التحسينات من الدقة

حسنا بعد حل المشكلة أخيرا مع فكل تؤثر على دقة القياس التي اكتشفت بفضل هذا السؤال وأكثر من ذلك هنا ، لتحسين دقة يمكنك قبل معيار القيام بذلك:

  1. تعيين فئة الأولوية العملية إلى realtime

  2. تعيين تقارب عملية لوحدة المعالجة المركزية واحد

    لذلك يمكنك قياس وحدة المعالجة المركزية واحد فقط على متعددة النوى

  3. مسح البيانات وتعليمات كاشيس

فمثلا:

    // before mem benchmark
    DWORD process_affinity_mask=0;
    DWORD system_affinity_mask =0;
    HANDLE hnd=GetCurrentProcess();
    if (hnd!=NULL)
        {
        // priority
        SetPriorityClass(hnd,REALTIME_PRIORITY_CLASS);
        // affinity
        GetProcessAffinityMask(hnd,&process_affinity_mask,&system_affinity_mask);
        process_affinity_mask=1;
        SetProcessAffinityMask(hnd,process_affinity_mask);
        GetProcessAffinityMask(hnd,&process_affinity_mask,&system_affinity_mask);
        }
    // flush CACHEs
    for (DWORD i=0;i<sizes[N-1];i+=7)
        {
        dat[i]+=i;
        dat[i]*=i;
        dat[i]&=i;
        }

    // after mem benchmark
    if (hnd!=NULL)
        {
        SetPriorityClass(hnd,NORMAL_PRIORITY_CLASS);
        SetProcessAffinityMask(hnd,system_affinity_mask);
        }

لذا فإن القياس الأكثر دقة يبدو كما يلي:


Answer #2

lengthMod متغير لا يفعل ما تظن أنه يفعل. تريد أن تحد من حجم مجموعة البيانات الخاصة بك، ولكن لديك 2 مشاكل هناك -

  • القيام بتدوين و مع قوة 2 سوف تخفي جميع البتات باستثناء واحد على أن. إذا كان على سبيل المثال lengthMod هو 1K (0x400)، ثم جميع المؤشرات أقل من 0x400 (بمعنى i = 1 إلى 63) ببساطة خريطة إلى الفهرس 0، لذلك عليك دائما ضرب ذاكرة التخزين المؤقت. وهذا هو السبب في أن النتائج سريعة جدا. بدلا من استخدام lengthMod - 1 لإنشاء قناع الصحيح (0x400 -> 0x3ff، التي من شأنها أن تخفي فقط بت العليا وترك أقل منها سليمة).
  • بعض القيم ل lengthMod ليست قوة من 2، وبالتالي فإن lengthMod-1 لن تعمل هناك حيث أن بعض بتات قناع لا تزال الأصفار. إما إزالتها من القائمة، أو استخدام عملية مودولو بدلا من lengthMod-1 تماما. انظر أيضا إجابتي هنا عن قضية مماثلة.

وهناك مشكلة أخرى هي أن القفزات 16B ربما لا تكفي لتخطي خط ذاكرة التخزين المؤقت لأن معظم وحدات المعالجة المركزية المشتركة تعمل مع 64 بايت الكاشلين، حتى تحصل على واحد فقط ملكة جمال لكل 4 التكرارات. استخدم (i*64) بدلا من ذلك.





cpu-cache