මූලද්‍රව්‍ය වශයෙන් එකතු කිරීම් ඒකාබද්ධ ලූපයකට වඩා වෙනම ලූපවල වේගවත් වන්නේ ඇයි?


2252

සිතන්න a1, b1, c1, හා d1ගොඩක් මතක සහ මගේ සංඛ්යාත්මක කේතය කිරීමට අදහස් පහත සඳහන් ප්රධාන පුඩුවක් ඇත.

const int n = 100000;

for (int j = 0; j < n; j++) {
    a1[j] += b1[j];
    c1[j] += d1[j];
}

මෙම ලූපය වෙනත් බාහිර forපුඩුවක් හරහා 10,000 වතාවක් ක්‍රියාත්මක වේ. එය වේගවත් කිරීම සඳහා, මම කේතය පහත පරිදි වෙනස් කළෙමි.

for (int j = 0; j < n; j++) {
    a1[j] += b1[j];
}

for (int j = 0; j < n; j++) {
    c1[j] += d1[j];
}

MS Visual C ++ 10.0 මත සම්පුර්ණ ප්‍රශස්තිකරණයකින් සහ SSE2 ඉන්ටෙල් කෝර් 2 ඩුඕ (x64) මත 32-බිට් සඳහා සක්‍රීය කර ඇති අතර , පළමු උදාහරණය තත්පර 5.5 ක් ගත වන අතර ද්විත්ව ලූප උදාහරණය ගත වන්නේ තත්පර 1.9 ක් පමණි. මගේ ප්‍රශ්නය: (කරුණාකර මගේ නැවත සකස් කළ ප්‍රශ්නය පහතින් බලන්න)

PS: මෙය විශ්වාස කරන්නේ නම් මට විශ්වාස නැත:

පළමු ලූපය සඳහා විසුරුවා හැරීම මූලික වශයෙන් මේ ආකාරයෙන් පෙනේ (මෙම වැඩසටහන සම්පූර්ණ වැඩසටහනේ පස් වතාවක් පමණ පුනරාවර්තනය වේ):

movsd       xmm0,mmword ptr [edx+18h]
addsd       xmm0,mmword ptr [ecx+20h]
movsd       mmword ptr [ecx+20h],xmm0
movsd       xmm0,mmword ptr [esi+10h]
addsd       xmm0,mmword ptr [eax+30h]
movsd       mmword ptr [eax+30h],xmm0
movsd       xmm0,mmword ptr [edx+20h]
addsd       xmm0,mmword ptr [ecx+28h]
movsd       mmword ptr [ecx+28h],xmm0
movsd       xmm0,mmword ptr [esi+18h]
addsd       xmm0,mmword ptr [eax+38h]

ද්විත්ව ලූප උදාහරණයේ සෑම පුඩුවක්ම මෙම කේතය නිපදවයි (පහත දැක්වෙන කොටස තුන් වතාවක් පමණ පුනරාවර්තනය වේ):

addsd       xmm0,mmword ptr [eax+28h]
movsd       mmword ptr [eax+28h],xmm0
movsd       xmm0,mmword ptr [ecx+20h]
addsd       xmm0,mmword ptr [eax+30h]
movsd       mmword ptr [eax+30h],xmm0
movsd       xmm0,mmword ptr [ecx+28h]
addsd       xmm0,mmword ptr [eax+38h]
movsd       mmword ptr [eax+38h],xmm0
movsd       xmm0,mmword ptr [ecx+30h]
addsd       xmm0,mmword ptr [eax+40h]
movsd       mmword ptr [eax+40h],xmm0

හැසිරීම අරා (n) සහ CPU හැඹිලියේ ප්‍රමාණයන් මත දැඩි ලෙස රඳා පවතින බැවින් ප්‍රශ්නය කිසිදු අදාල නොවේ. එබැවින් වැඩි උනන්දුවක් ඇත්නම්, මම ප්‍රශ්නය නැවත ලියමි:

පහත දැක්වෙන ප්‍රස්ථාරයේ කලාප පහ විසින් නිරූපණය කර ඇති පරිදි විවිධ හැඹිලි හැසිරීම් වලට තුඩු දෙන විස්තර පිළිබඳව ඔබට යම් අවබෝධයක් ලබා දිය හැකිද?

මෙම CPU සඳහා සමාන ප්‍රස්ථාරයක් ලබා දීමෙන් CPU / හැඹිලි ගෘහ නිර්මාණ ශිල්පය අතර ඇති වෙනස්කම් පෙන්වා දීම සිත්ගන්නා සුළු විය හැකිය.

PPS: මෙන්න සම්පූර්ණ කේතය. ඉහළ විභේදන වේලාවක් සඳහා එය TBB භාවිතා කරයි Tick_Count, එය TBB_TIMINGසාර්ව අර්ථ දැක්වීමෙන් අක්‍රිය කළ හැකිය :

#include <iostream>
#include <iomanip>
#include <cmath>
#include <string>

//#define TBB_TIMING

#ifdef TBB_TIMING   
#include <tbb/tick_count.h>
using tbb::tick_count;
#else
#include <time.h>
#endif

using namespace std;

//#define preallocate_memory new_cont

enum { new_cont, new_sep };

double *a1, *b1, *c1, *d1;


void allo(int cont, int n)
{
    switch(cont) {
      case new_cont:
        a1 = new double[n*4];
        b1 = a1 + n;
        c1 = b1 + n;
        d1 = c1 + n;
        break;
      case new_sep:
        a1 = new double[n];
        b1 = new double[n];
        c1 = new double[n];
        d1 = new double[n];
        break;
    }

    for (int i = 0; i < n; i++) {
        a1[i] = 1.0;
        d1[i] = 1.0;
        c1[i] = 1.0;
        b1[i] = 1.0;
    }
}

void ff(int cont)
{
    switch(cont){
      case new_sep:
        delete[] b1;
        delete[] c1;
        delete[] d1;
      case new_cont:
        delete[] a1;
    }
}

double plain(int n, int m, int cont, int loops)
{
#ifndef preallocate_memory
    allo(cont,n);
#endif

#ifdef TBB_TIMING   
    tick_count t0 = tick_count::now();
#else
    clock_t start = clock();
#endif

    if (loops == 1) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++){
                a1[j] += b1[j];
                c1[j] += d1[j];
            }
        }
    } else {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                a1[j] += b1[j];
            }
            for (int j = 0; j < n; j++) {
                c1[j] += d1[j];
            }
        }
    }
    double ret;

#ifdef TBB_TIMING   
    tick_count t1 = tick_count::now();
    ret = 2.0*double(n)*double(m)/(t1-t0).seconds();
#else
    clock_t end = clock();
    ret = 2.0*double(n)*double(m)/(double)(end - start) *double(CLOCKS_PER_SEC);
#endif

#ifndef preallocate_memory
    ff(cont);
#endif

    return ret;
}


void main()
{   
    freopen("C:\\test.csv", "w", stdout);

    char *s = " ";

    string na[2] ={"new_cont", "new_sep"};

    cout << "n";

    for (int j = 0; j < 2; j++)
        for (int i = 1; i <= 2; i++)
#ifdef preallocate_memory
            cout << s << i << "_loops_" << na[preallocate_memory];
#else
            cout << s << i << "_loops_" << na[j];
#endif

    cout << endl;

    long long nmax = 1000000;

#ifdef preallocate_memory
    allo(preallocate_memory, nmax);
#endif

    for (long long n = 1L; n < nmax; n = max(n+1, long long(n*1.2)))
    {
        const long long m = 10000000/n;
        cout << n;

        for (int j = 0; j < 2; j++)
            for (int i = 1; i <= 2; i++)
                cout << s << plain(n, m, j, i);
        cout << endl;
    }
}

(එය විවිධ අගයන් සඳහා FLOP / s පෙන්වයි n.)

රූප විස්තරය මෙහි ඇතුළත් කරන්න


4
ඔබ භෞතික ප්‍රවේශයට පිවිසෙන සෑම අවස්ථාවකම සෙවීමේදී මන්දගාමී වන මෙහෙයුම් පද්ධතිය විය හැකි අතර එකම පටලයකට ද්විතියික ප්‍රවේශයක් ඇති විට හැඹිලිය වැනි යමක් තිබේ.
ඇලෙක්ස්ටියෝ

7
ඔබ ප්‍රශස්තිකරණය සමඟ සම්පාදනය කරනවාද? එය O2 සඳහා බොහෝ asm කේතයක් සේ පෙනේ ...
Luchian Grigore

1
මීට කලකට පෙර සමාන ප්‍රශ්නයක් ලෙස පෙනෙන්නේ කුමක්දැයි මම විමසුවෙමි . එය හෝ පිළිතුරු වලට උනන්දුවක් දක්වන තොරතුරු තිබිය හැකිය.
මාර්ක් විල්කින්ස්

61
අච්චාරු කිරීමට නම්, අතිච්ඡාදනය වන දර්ශක නිසා මෙම කේත ස්නිපෙට් දෙක සමාන නොවේ. C99 restrictඑවැනි තත්වයන් සඳහා මූලික පදය ඇත. MSVC හා සමාන දෙයක් තිබේදැයි මම නොදනිමි. ඇත්ත වශයෙන්ම, මෙය ගැටළුව නම් SSE කේතය නිවැරදි නොවේ.
user510306

8
මතක අන්වර්ථකරණය සමඟ මෙය සම්බන්ධ විය හැකිය. එක් ලූපයක් d1[j]සමඟ a1[j], අන්වර්ථ විය හැක , එබැවින් සම්පාදකයා සමහර මතක ප්‍රශස්තිකරණයෙන් ඉවත් විය හැකිය. ඔබ ලියවිලි මතකයට ලූප දෙකකින් වෙන් කළහොත් එය සිදු නොවේ.
rturrado

Answers:


1696

මේ පිළිබඳ වැඩිදුර විශ්ලේෂණය කිරීමෙන් පසු, මෙය (අවම වශයෙන් අර්ධ වශයෙන්) සිව්-ලක්ෂ්‍යයන්ගේ දත්ත පෙළගැස්ම නිසා ඇති වූවක් යැයි මම විශ්වාස කරමි. මෙය යම් මට්ටමක හැඹිලි බැංකු / මාර්ග ගැටුම් ඇති කරයි.

ඔබ ඔබේ අරා වෙන් කරන්නේ කෙසේදැයි මම නිවැරදිව අනුමාන කර ඇත්නම්, ඒවා පිටු පේළියට පෙළ ගැසෙනු ඇත.

මෙයින් අදහස් කරන්නේ සෑම ලූපයකම ඔබගේ සියලුම ප්‍රවේශයන් එකම හැඹිලි ක්‍රමයට වැටෙනු ඇති බවයි. කෙසේ වෙතත්, ඉන්ටෙල් ප්‍රොසෙසරයන් සඳහා 8-මාර්ග L1 හැඹිලි ඇසෝසියේටිව් කාලයක් ඇත. නමුත් යථාර්ථය නම්, කාර්ය සාධනය සම්පූර්ණයෙන්ම ඒකාකාරී නොවේ. ද්වි-මාර්ග යැයි පැවසීමට වඩා 4-මාර්ග වලට ප්‍රවේශ වීම තවමත් මන්දගාමී ය.

සංස්කරණය කරන්න: ඇත්ත වශයෙන්ම ඔබ සියලු අරා වෙන වෙනම වෙන් කරන බවක් පෙනේ. සාමාන්‍යයෙන් එවැනි විශාල ප්‍රතිපාදන ඉල්ලා සිටින විට, විබෙදන්නා මෙහෙයුම් පද්ධතියෙන් නැවුම් පිටු ඉල්ලනු ඇත. එම නිසා, පිටු සීමාවෙන් විශාල ඕෆ්සෙට් එකක විශාල ප්‍රතිපාදන දිස්වීමට වැඩි අවස්ථාවක් තිබේ.

පරීක්ෂණ කේතය මෙන්න:

int main(){
    const int n = 100000;

#ifdef ALLOCATE_SEPERATE
    double *a1 = (double*)malloc(n * sizeof(double));
    double *b1 = (double*)malloc(n * sizeof(double));
    double *c1 = (double*)malloc(n * sizeof(double));
    double *d1 = (double*)malloc(n * sizeof(double));
#else
    double *a1 = (double*)malloc(n * sizeof(double) * 4);
    double *b1 = a1 + n;
    double *c1 = b1 + n;
    double *d1 = c1 + n;
#endif

    //  Zero the data to prevent any chance of denormals.
    memset(a1,0,n * sizeof(double));
    memset(b1,0,n * sizeof(double));
    memset(c1,0,n * sizeof(double));
    memset(d1,0,n * sizeof(double));

    //  Print the addresses
    cout << a1 << endl;
    cout << b1 << endl;
    cout << c1 << endl;
    cout << d1 << endl;

    clock_t start = clock();

    int c = 0;
    while (c++ < 10000){

#if ONE_LOOP
        for(int j=0;j<n;j++){
            a1[j] += b1[j];
            c1[j] += d1[j];
        }
#else
        for(int j=0;j<n;j++){
            a1[j] += b1[j];
        }
        for(int j=0;j<n;j++){
            c1[j] += d1[j];
        }
#endif

    }
    
    clock_t end = clock();
    cout << "seconds = " << (double)(end - start) / CLOCKS_PER_SEC << endl;

    system("pause");
    return 0;
}

මිණුම් සලකුණු ප්‍රති Results ල:

සංස්කරණය කරන්න: සත්‍ය Core 2 ගෘහ නිර්මාණ යන්ත්‍රයක ප්‍රති Results ල :

2 x Intel Xeon X5482 Harpertown @ 3.2 GHz:

#define ALLOCATE_SEPERATE
#define ONE_LOOP
00600020
006D0020
007A0020
00870020
seconds = 6.206

#define ALLOCATE_SEPERATE
//#define ONE_LOOP
005E0020
006B0020
00780020
00850020
seconds = 2.116

//#define ALLOCATE_SEPERATE
#define ONE_LOOP
00570020
00633520
006F6A20
007B9F20
seconds = 1.894

//#define ALLOCATE_SEPERATE
//#define ONE_LOOP
008C0020
00983520
00A46A20
00B09F20
seconds = 1.993

නිරීක්ෂණ:

  • තත්පර 6,206 එක් පුඩුවක් හා සමග තත්පර 2,116 වළළු දෙකක්. මෙය OP හි ප්‍රති results ල හරියටම ප්‍රතිනිෂ්පාදනය කරයි.

  • පළමු පරීක්ෂණ දෙකේදී, අරා වෙන වෙනම වෙන් කරනු ලැබේ. පිටුවට සාපේක්ෂව ඔවුන් සියල්ලන්ටම එක හා සමාන පෙළගැස්මක් ඇති බව ඔබට පෙනෙනු ඇත.

  • දෙවන පරීක්ෂණ දෙකෙහිදී, එම පෙළගැස්ම බිඳ දැමීම සඳහා අරා එකට ඇසුරුම් කර ඇත. ලූප දෙකම වේගවත් බව මෙහිදී ඔබට පෙනෙනු ඇත. තවද, ඔබ සාමාන්‍යයෙන් අපේක්ෂා කරන පරිදි දෙවන (ද්විත්ව) ලූපය දැන් මන්දගාමී වේ.

St ස්ටෙෆන් කැනන් අදහස් දැක්වීමේදී පෙන්වා දෙන පරිදි, මෙම පෙළගැස්ම මඟින් බර / ගබඩා ඒකක හෝ හැඹිලියේ ව්‍යාජ අන්වර්ථයක් ඇතිවීමට බොහෝ දුරට ඉඩ තිබේ . මම මේ සඳහා ගූගල් කර බැලූ විට ඉන්ටෙල් ඇත්ත වශයෙන්ම අර්ධ ලිපින අන්වර්ථ කුටි සඳහා දෘඩාංග කවුන්ටරයක් ​​ඇති බව සොයා ගත්තා :

http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/~amplifierxe/pmw_dp/events/partial_address_alias.html


කලාප 5 - පැහැදිලි කිරීම්

කලාපය 1:

මෙය පහසුය. දත්ත කට්ටලය කොතරම් කුඩාද යත්, කාර්ය සාධනය ආධිපත්‍යය දරන්නේ ලූප සහ අතු වැනි ය.

කලාපය 2:

මෙන්න, දත්ත ප්‍රමාණය වැඩි වන විට සාපේක්ෂ පොදු කාර්ය ප්‍රමාණය අඩු වන අතර කාර්ය සාධනය "සංතෘප්ත" වේ. මෙහි ලූප දෙකක් මන්දගාමී වන්නේ එයට ලූප මෙන් දෙගුණයක් හා ඉහළින් අතු බෙදී ඇති බැවිනි.

මෙහි සිදුවන්නේ කුමක්දැයි මට හරියටම විශ්වාස නැත ... ඇග්නර් ෆෝග් විසින් හැඹිලි බැංකු ගැටුම් ගැන සඳහන් කරන බැවින් පෙළගැස්ම තවමත් බලපෑමක් ඇති කරයි . (එම සබැඳිය සැන්ඩි බ්‍රිජ් ගැන ය, නමුත් අදහස තවමත් කෝර් 2 ට අදාළ විය යුතුය.)

කලාපය 3:

මෙම අවස්ථාවෙහිදී, දත්ත තවදුරටත් L1 හැඹිලියට නොගැලපේ. එබැවින් කාර්ය සාධනය L1 <-> L2 හැඹිලි කලාප පළල මගින් ආවරණය කරයි.

කලාපය 4:

තනි පුඩුවේ කාර්ය සාධනය පහත වැටීම යනු අප නිරීක්ෂණය කරන දෙයයි. සඳහන් කළ පරිදි, මෙය සිදුවන්නේ පෙළගැස්ම නිසා (බොහෝ දුරට) ප්‍රොසෙසරයේ බර / ගබඩා ඒකකවල ව්‍යාජ අන්වර්ථ කුටි ඇති කරයි.

කෙසේ වෙතත්, ව්‍යාජ අන්වර්ථයක් සිදුවීමට නම්, දත්ත කට්ටල අතර ප්‍රමාණවත් තරම් විශාල පියවරක් තිබිය යුතුය. 3 වන කලාපයේ ඔබ මෙය නොදකින්නේ මේ නිසාය.

කලාපය 5:

මෙම අවස්ථාවෙහිදී, හැඹිලියට කිසිවක් නොගැලපේ. එබැවින් ඔබ මතක කලාප පළලකින් බැඳී ඇත.


2 x Intel X5482 Harpertown @ 3.2 GHz Intel Core i7 870 @ 2.8 GHz Intel Core i7 2600K @ 4.4 GHz


162
+1: මම හිතන්නේ මේක තමයි පිළිතුර. අනෙක් සියලුම පිළිතුරු පවසන දෙයට පටහැනිව, එය සහජයෙන්ම වැඩි හැඹිලි මිස් ඇති තනි ලූප ප්‍රභේදය ගැන නොවේ, එය හැඹිලිය මඟ හැරීමට හේතු වන අරා වල විශේෂිත පෙළගැස්වීම ගැන ය.
ඔලිවර් චාල්ස්වර්ත්

30
මෙය; එය බොරු අනුවර්ත නාමකරණයට ප්රදර්ශන කුටිය ඉතාමත් සරළ පැහැදිලි කිරීමක්.
ස්ටීවන් කැනන්

7
IctVictorT. මම OP සම්බන්ධිත කේතය භාවිතා කළෙමි. එය .css ගොනුවක් ජනනය කරන අතර එය මට එක්සෙල් හි විවෘත කර එයින් ප්‍රස්ථාරයක් සෑදිය හැකිය.
අද්භූත

5
Aw නවාස් පිටුවක් සාමාන්‍යයෙන් 4KB වේ. මා මුද්‍රණය කරන ෂඩාස්රාකාර ලිපින දෙස බැලුවහොත්, වෙන වෙනම වෙන් කර ඇති පරීක්ෂණ සියල්ලටම එකම මොඩියුලෝ 4096 ඇත. එමඟින් ඔබට වෙනස්කම් නොපෙනෙන්නේ මන්දැයි පැහැදිලි කළ හැකිය.
ගුප්ත


226

හරි, නිවැරදි පිළිතුර අනිවාර්යයෙන්ම CPU හැඹිලිය සමඟ යමක් කළ යුතුය. නමුත් හැඹිලි තර්කය භාවිතා කිරීම තරමක් අපහසු විය හැකිය, විශේෂයෙන් දත්ත නොමැතිව.

බොහෝ පිළිතුරු ඇත, එය විශාල සාකච්ඡාවකට තුඩු දුන් නමුත් අපි එයට මුහුණ දෙමු: හැඹිලි ගැටළු ඉතා සංකීර්ණ විය හැකි අතර එය එක් මානයක් නොවේ. ඒවා දත්තවල විශාලත්වය මත බෙහෙවින් රඳා පවතී, එබැවින් මගේ ප්‍රශ්නය අසාධාරණ විය: එය හැඹිලි ප්‍රස්ථාරයේ ඉතා රසවත් ස්ථානයක විය.

My ගුප්ත විද්‍යාවේ පිළිතුර බොහෝ දෙනෙකුට (මා ද ඇතුළුව) ඒත්තු ගැන්වූයේ, බොහෝ විට එය කරුණු මත රඳා පවතින බවක් පෙනෙන්නට තිබුණත් එය සත්‍යයේ එක් “දත්ත ලක්ෂ්‍යයක්” පමණක් වූ බැවිනි.

ඒ නිසා මම ඔහුගේ පරීක්ෂණය (අඛණ්ඩ එදිරිව වෙනම වෙන් කිරීමක් භාවිතා කරමින්) සහ @ ජේම්ස්ගේ පිළිතුරේ උපදෙස් ඒකාබද්ධ කළෙමි.

පහත දැක්වෙන ප්‍රස්ථාර වලින් පෙන්නුම් කරන්නේ, බොහෝ පිළිතුරු සහ විශේෂයෙන් ප්‍රශ්න හා පිළිතුරු සඳහා බහුතරයක් අදහස් දැක්වීම හරියටම භාවිතා කළ නිශ්චිත තත්වය සහ පරාමිතීන් මත පදනම්ව සම්පූර්ණයෙන්ම වැරදි හෝ සත්‍ය ලෙස සැලකිය හැකි බවයි.

මගේ ආරම්භක ප්‍රශ්නය n = 100.000 බව සලකන්න . මෙම කරුණ (අහම්බෙන්) විශේෂ හැසිරීම් පෙන්නුම් කරයි:

  1. එය ලූප් කළ අනුවාදය සහ දෙක අතර විශාලතම විෂමතාවය ඇත (පාහේ තුනක සාධකයකි)

  2. එක් ලූපයක් (එනම් අඛණ්ඩව වෙන් කිරීම සමඟ) ද්වි-ලූප අනුවාදය පරාජය කරන එකම ලක්ෂ්‍යය එයයි. (මෙය ගුප්ත විද්‍යාවේ පිළිතුර කිසිසේත් කළ නොහැකි විය.)

ආරම්භක දත්ත භාවිතා කිරීමේ ප්‍රති result ලය:

රූප විස්තරය මෙහි ඇතුළත් කරන්න

ආරම්භ නොකළ දත්ත භාවිතා කිරීමේ ප්‍රති result ලය (ගුප්ත පරීක්‍ෂණය මෙයයි):

රූප විස්තරය මෙහි ඇතුළත් කරන්න

මෙය පැහැදිලි කිරීමට අපහසු එකකි: ආරම්භක දත්ත, එක් වරක් වෙන් කර විවිධ දෛශික ප්‍රමාණයේ පහත සඳහන් සෑම පරීක්ෂණ අවස්ථාවකටම නැවත භාවිතා කරයි:

රූප විස්තරය මෙහි ඇතුළත් කරන්න

යෝජනාව

තොග පිටාර ගැලීම පිළිබඳ සෑම පහත් මට්ටමේ කාර්ය සාධනය හා සම්බන්ධ සෑම ප්‍රශ්නයක්ම මුළු දත්ත පරාසයේ හැඹිලි අදාළ දත්ත ප්‍රමාණයන් සඳහා MFLOPS තොරතුරු සැපයීමට අවශ්‍ය විය යුතුය! පිළිතුරු ගැන සිතීම සහ විශේෂයෙන් මෙම තොරතුරු නොමැතිව අන් අය සමඟ සාකච්ඡා කිරීම සෑම කෙනෙකුගේම කාලය නාස්තියකි.


18
+1 නියම විශ්ලේෂණය. දත්ත මුලින් ආරම්භ නොකිරීමට මම අදහස් නොකළෙමි. එය සිදු වූයේ, කෙසේ වෙතත්, වෙන් කරන්නා ඒවා ශුන්‍ය කර ඇත. එබැවින් ආරම්භක දත්ත වැදගත් වේ. මම මගේ පිළිතුර සත්‍ය Core 2 ගෘහ නිර්මාණ යන්ත්‍රයක ප්‍රති results ල සමඟ සංස්කරණය කළ අතර ඒවා ඔබ නිරීක්ෂණය කරන දෙයට වඩා බොහෝ සමීප ය. තවත් දෙයක් නම්, මම ප්‍රමාණයේ පරාසයක් පරීක්ෂා කළ nඅතර එය n = 80000, n = 100000, n = 200000යනාදිය සඳහා එකම ක්‍රියාකාරී පරතරය පෙන්වයි ...
ගුප්ත

2
Y ගුප්ත විද්‍යාව මම සිතන්නේ ක්‍රියාවලියක් සඳහා නව පිටු ලබා දෙන සෑම අවස්ථාවකදීම මෙහෙයුම් පද්ධතිය පිටු ශුන්‍ය කිරීම ක්‍රියාත්මක කරන බවයි.
v.oddou

1
@ v.oddou: හැසිරීම මෙහෙයුම් පද්ධතිය මත ද රඳා පවතී; IIRC, වින්ඩෝස් සතුව ශුන්‍යයෙන් නිදහස් වූ පිටු පසුබිමට නූල් ඇති අතර, ඉල්ලීමක් දැනටමත් ශුන්‍ය කළ පිටු වලින් සෑහීමකට පත්විය නොහැකි නම්, ඉල්ලීම VirtualAllocසපුරාලීමට ප්‍රමාණවත් තරම් ශුන්‍ය වන තෙක් ඇමතුම් අවහිර කරයි. ඊට වෙනස්ව, ලිනක්ස් ශුන්‍ය පිටුව අවශ්‍ය පරිදි පිටපත්-මත-ලිවීම ලෙස සිතියම් ගත කරන අතර, ලිවීමේදී, එය නව දත්ත ලිවීමට පෙර නව ශුන්‍යයන් නව පිටුවකට පිටපත් කරයි. කෙසේ වෙතත්, පරිශීලක මාදිලියේ ක්‍රියාවලියේ දෘෂ්ටි කෝණයෙන්, පිටු ශුන්‍ය වේ, නමුත් ආරම්භක නොවන මතකයේ පළමු භාවිතය සාමාන්‍යයෙන් වින්ඩෝස් වලට වඩා ලිනක්ස් මත මිල අධික වනු ඇත.
ෂැඩෝ රේන්ජර්

82

දෙවන පුඩුවට අඩුවෙන් හැඹිලි ක්‍රියාකාරකම් ඇතුළත් වන බැවින් ප්‍රොසෙසරයට මතක ඉල්ලීම් සපුරාලීම පහසුය.


1
ඔබ කියන්නේ දෙවන ප්‍රභේදයට හැඹිලි අඩු වීමක් ඇති බවයි? මන්ද?
ඔලිවර් චාල්ස්වර්ත්

2
@Oli: පළමු ප්රභේද්යයක් දී, සකසනය ප්රවේශ කිරීමට time- දී මතක රේඛා හතරක් අවශ්ය a[i], b[i], c[i]හා d[i]දෙවන ප්රභේද්යයක් දී, එය දෙකක් අවශ්ය වේ. එකතු කරන අතරතුර එම රේඛා නැවත පිරවීම මෙමඟින් වඩාත් ශක්‍ය වේ.
බලු පැටියා

4
නමුත් අරා හැඹිලියේ ගැටෙන්නේ නැති තාක් කල්, එක් එක් ප්‍රභේදයට හරියටම එකම මතක කියවීම් සහ ලිවීම් සංඛ්‍යාවක් අවශ්‍ය වේ. ඉතින් නිගමනය නම් (මම හිතන්නේ) මෙම අරා දෙක නිතරම ගැටෙන බවයි.
ඔලිවර් චාල්ස්වර්ත්

3
මම අනුගමනය කරන්නේ නැහැ. එක් උපදෙස් සඳහා (එනම් එක් අවස්ථාවකට x += y), කියවීම් දෙකක් සහ එක් ලිවීමක් ඇත. එක්කෝ ප්‍රභේදයක් සඳහා මෙය සත්‍ය වේ. හැඹිලිය <-> CPU කලාප පළල අවශ්‍යතාවය සමාන වේ. ගැටුම් නොමැති තාක් කල්, හැඹිලිය <-> RAM කලාප පළල අවශ්‍යතාවය ද එක හා සමානයි ..
ඔලිවර් චාල්ස්වර්ත්

2
Stackoverflow.com/a/1742231/102916 හි සඳහන් කර ඇති පරිදි , පෙන්ටියම් එම් හි දෘඩාංග උපසර්ගයට විවිධ ඉදිරි ප්‍රවාහ 12 ක් නිරීක්ෂණය කළ හැකිය (පසුව දෘඩාංග අවම වශයෙන් හැකියාවක් ඇතැයි මම බලාපොරොත්තු වෙමි). ලූප් 2 තවමත් කියවන්නේ ධාරා හතරක් පමණි, එම සීමාව තුළ එය හොඳින් පවතී.
මෝසෙස්

51

nඔබේ අරා දෙකක් එකවර මතකයේ තබා ගත හැකි වන පරිදි ඔබ නිවැරදි යන්ත්‍රයක් මත වැඩ කරමින් සිටින බව සිතන්න , නමුත් තැටි හැඹිලිය හරහා ලබා ගත හැකි මුළු මතකය තවමත් සතරම රඳවා තබා ගැනීමට ප්‍රමාණවත් විය.

සරල LIFO හැඹිලි ප්‍රතිපත්තියක් උපකල්පනය කරමින්, මෙම කේතය:

for(int j=0;j<n;j++){
    a[j] += b[j];
}
for(int j=0;j<n;j++){
    c[j] += d[j];
}

පළමු හේතුව බව aහා bRAM පටවා පසුව RAM තුළ සම්පූර්ණයෙන්ම මත වැඩ කළ යුතුය. විට දෙවන පුඩුවක් ආරම්භ, cසහ dඑවකට RAM බවට තැටිය පූර්ණය හා ක්රියාත්මක වනු ඇත.

අනෙක් ලූපය

for(int j=0;j<n;j++){
    a[j] += b[j];
    c[j] += d[j];
}

සෑම අවස්ථාවකදීම ලූපය වටා අරා දෙකක් සහ අනෙක් දෙකෙහි පිටුවක් පිට කරයි . මෙය පැහැදිලිවම වඩා මන්දගාමී වනු ඇත .

ඔබගේ පරීක්ෂණ වලදී තැටි හැඹිලිය ඔබ නොදකින නමුත් වෙනත් ආකාරයක හැඹිලි වල අතුරු ආබාධ ඔබ දැක ඇති.


මෙහි කුඩා ව්‍යාකූලත්වයක් / වරදවා වටහා ගැනීමක් ඇති බව පෙනේ, එබැවින් මම උදාහරණයක් භාවිතා කර ටිකක් විස්තර කිරීමට උත්සාහ කරමි.

කියන්න n = 2, අපි බයිට් සමඟ වැඩ කරන්නෙමු. මගේ තත්වය අනුව අපට ඇත්තේ RAM බයිට් 4 ක් පමණක් වන අතර අපගේ මතකයේ ඉතිරි කොටස සැලකිය යුතු ලෙස මන්දගාමී වේ (100 ගුණයකින් වැඩි ප්‍රවේශයක් යැයි කියන්න).

බයිට් හැඹිලියේ නොමැති නම් තරමක් ගොළු හැඹිලි ප්‍රතිපත්තියක් උපකල්පනය කරමින් , එය එහි තබා පහත සඳහන් බයිටය ද අප එහි සිටින විට ලබා ගන්න. ඔබට මෙවැනි දෙයක් ලැබෙනු ඇත:

  • සමඟ

    for(int j=0;j<n;j++){
     a[j] += b[j];
    }
    for(int j=0;j<n;j++){
     c[j] += d[j];
    }
  • හැඹිලි a[0]හා a[1]පසුව b[0]සහ b[1]සහ කුලකය a[0] = a[0] + b[0]පූර්වාපේක්ෂීව සංචිත කෙරී - බයිට් වලින් හැඹිලි හතර දැන් එහි වන අතර, a[0], a[1]සහ b[0], b[1]. පිරිවැය = 100 + 100.

  • a[1] = a[1] + b[1]හැඹිලියේ සකසා ඇත. පිරිවැය = 1 + 1.
  • සඳහා නැවත නැවත cහා d.
  • මුළු පිරිවැය = (100 + 100 + 1 + 1) * 2 = 404

  • සමඟ

    for(int j=0;j<n;j++){
     a[j] += b[j];
     c[j] += d[j];
    }
  • හැඹිලි a[0]හා a[1]පසුව b[0]සහ b[1]සහ කුලකය a[0] = a[0] + b[0]පූර්වාපේක්ෂීව සංචිත කෙරී - බයිට් වලින් හැඹිලි හතර දැන් එහි වන අතර, a[0], a[1]සහ b[0], b[1]. පිරිවැය = 100 + 100.

  • eject a[0], a[1], b[0], b[1]හැඹිලි සහ හැඹිලි සිට c[0]හා c[1]පසුව d[0]සහ d[1]සහ කුලකය c[0] = c[0] + d[0]පූර්වාපේක්ෂීව සංචිත කෙරී. පිරිවැය = 100 + 100.
  • මම හිතන්නේ ඔයා මම යන තැන බලන්න පටන් අරන් කියලා.
  • මුළු පිරිවැය = (100 + 100 + 100 + 100) * 2 = 800

මෙය සම්භාව්‍ය හැඹිලි පහරක්.


12
මෙය වැරදිය. අරාවෙහි විශේෂිත මූලද්‍රව්‍යයක් පිළිබඳ සඳහනක් මඟින් මුළු අරාව තැටියෙන් (හෝ හැඹිලි නොවන මතකයෙන්) පේජ් කිරීමට හේතු නොවේ; අදාළ පිටුව හෝ හැඹිලි රේඛාව පමණක් පේජ් කර ඇත.
ok ක්ස් මෝසෙස්

1
Ro බෘක්ස් මෝසෙස් - මෙහි සිදුවන ආකාරයටම ඔබ මුළු අරාව හරහා ගමන් කළහොත් එය සිදුවනු ඇත.
OldCurmudgeon

1
හොඳයි, ඔව්, නමුත් මුළු මෙහෙයුම පුරාම සිදුවන්නේ එය මිස ලූපය වටා සිදුවන සෑම අවස්ථාවකම නොවේ. දෙවන පෝරමය "ලූපය වටා සෑම අවස්ථාවකම අරා දෙකක් සහ අනෙක් දෙකෙහි පිටුවක් පිටවන බව" ඔබ කියා සිටි අතර මම එයට විරුද්ධ වන්නේ එයයි. සමස්ත අරා වල ප්‍රමාණය කුමක් වුවත්, මෙම ලූපය මධ්‍යයේ ඔබේ RAM එක එක් එක් අරා හතරෙන් පිටුවක් රඳවා තබා ගනු ඇති අතර, ලූපය අවසන් වූ පසු කිසිවක් පිටතට නොයනු ඇත.
ok ක්ස් මෝසෙස්

N යනු නිවැරදි අගය පමණක් වූ විශේෂිත අවස්ථාවකදී , ඔබේ අරා දෙකක් එකවර මතකයේ තබා ගත හැකි නම්, පසුව අරා හතරක සියලුම මූලද්‍රව්‍යයන් එක ලූපයකට ප්‍රවේශ වීම නිසැකවම අවසන් විය යුතුය.
OldCurmudgeon

1
ඔබ එක් එක් පිටුවෙහි මුල් පිටුවට වඩා පිටු 2 ක් සම්පූර්ණ a1හා b1පළමු පැවරුම සඳහා තබා ගන්නේ ඇයි? (ඔබ බයිට් පිටු 5 ක් උපකල්පනය කරයි, එබැවින් පිටුවක් ඔබගේ RAM වලින් අඩක් ද? එය පරිමාණය කිරීම පමණක් නොවේ, එය සැබෑ ප්‍රොසෙසරයකට වඩා වෙනස් ය.)
ok ක්ස් මෝසෙස්

36

එය වෙනස් කේතයක් නිසා නොව, හැඹිලිගත කිරීම නිසා ය: CPU රෙජිස්ටරයට වඩා RAM වේගය අඩු වන අතර විචල්‍යයක් වෙනස් වන සෑම අවස්ථාවකම RAM ලිවීම වළක්වා ගැනීම සඳහා හැඹිලි මතකය CPU තුළ ඇත. නමුත් හැඹිලිය RAM තරම් විශාල නොවේ, එබැවින් එය සිතියම් ගත කරන්නේ එයින් ඉතා සුළු කොටසක් පමණි.

පළමු කේතය දුරස්ථ මතක ලිපින සෑම ලූපයකම වෙනස් කරමින් වෙනස් කරයි, එමඟින් හැඹිලිය අවලංගු කිරීමට අඛණ්ඩව අවශ්‍ය වේ.

දෙවන කේතය විකල්ප නොවේ: එය යාබද ලිපින මත දෙවරක් ගලා යයි. මෙමඟින් හැඹිලියේ සියලුම කාර්යයන් සම්පූර්ණ කළ හැකි අතර එය අවලංගු කරන්නේ දෙවන ලූපය ආරම්භ වූ පසුව පමණි.


හැඹිලිය අඛණ්ඩව අවලංගු වීමට මෙය හේතු වන්නේ ඇයි?
ඔලිවර් චාල්ස්වර්ත්

1
Li ඔලිකාර්ල්ස්වර්ත්: හැඹිලිය පරස්පර මතක ලිපිනයන්හි දෘ copy පිටපතක් ලෙස සිතන්න. ඔබ ඒවායින් කොටසක් නොවන ලිපිනයකට පිවිසෙන ලෙස මවාපානවා නම්, ඔබට හැඹිලිය නැවත පූරණය කළ යුතුය. හැඹිලියේ යමක් වෙනස් කර ඇත්නම්, එය නැවත RAM වලින් ලිවිය යුතුය, නැතහොත් එය නැති වී යනු ඇත. නියැදි කේතයේ, 100'000 පූර්ණ සංඛ්‍යා (4kBytes) 4 දෛශිකය L1 හැඹිලියේ (128 හෝ 256K) ධාරිතාවට වඩා වැඩිය.
එමිලියෝ ගරාවග්ලියා

5
හැඹිලියේ ප්‍රමාණයට මෙම තත්වය තුළ කිසිදු බලපෑමක් නැත. සෑම අරා මූලද්‍රව්‍යයක්ම භාවිතා කරනුයේ එක් වරක් පමණක් වන අතර ඉන් පසුව එය ඉවත් කරනු ලැබුවහොත් එය වැදගත් නොවේ. හැඹිලි ප්‍රමාණය වැදගත් වන්නේ ඔබට තාවකාලික ප්‍රදේශයක් තිබේ නම් පමණි (එනම් ඔබ අනාගතයේදී එකම මූලද්‍රව්‍ය නැවත භාවිතා කිරීමට යන්නේ).
ඔලිවර් චාල්ස්වර්ත්

2
Li ඔලිකාර්ල්ස්වර්ත්: මට නව අගයක් හැඹිලියක පැටවීමට සිදුවී ඇත්නම් සහ එහි දැනටමත් වෙනස් කර ඇති අගයක් තිබේ නම්, මම එය මුලින්ම ලිවිය යුතු අතර, මෙය ලිවීම සිදුවන තෙක් බලා සිටිමි.
එමිලියෝ ගරාවග්ලියා

2
නමුත් OP කේතයේ ප්‍රභේද දෙකෙහිම, එක් එක් අගය හරියටම එක් වරක් වෙනස් වේ. ඔබ එසේ කරන්නේ එක් එක් ප්‍රභේදයේ ඇති එකම ලිවීම් ගණනකි.
ඔලිවර් චාල්ස්වර්ත්

23

මෙහි සාකච්ඡා කළ ප්‍රති results ල මට අනුකරණය කළ නොහැක.

දුර්වල මිණුම් දණ්ඩ කේතයට දොස් පැවරිය යුතුද, නැතහොත් කුමක් දැයි මම නොදනිමි, නමුත් ක්‍රම දෙක මගේ පරිගණකයේ 10% ක් තුළ පහත කේතය භාවිතා කරයි, එක් ලූපයක් සාමාන්‍යයෙන් දෙකට වඩා තරමක් වේගවත් වේ - ඔබ සිතන ආකාරයට බලාපොරොත්තු වන්න.

අරා ප්‍රමාණ 2 ^ 16 සිට 2 ^ 24 දක්වා වූ අතර ලූප අටක් භාවිතා කරයි. ප්‍රභව අරා ආරම්භ කිරීමට මම ප්‍රවේශම් විය, එබැවින් +=පැවරුම FPU වෙතින් මතක කුණු කසළ දෙගුණයක් ලෙස එකතු කරන ලෙස ඉල්ලා නොසිටියේය .

මම එවැනි පැවරුම දමා විවිධ යෝජනා ක්රම, සමග පමණ ඉටු b[j], d[j]කිරීමට InitToZero[j]වළළු තුළ, ද භාවිතා සමග += b[j] = 1හා += d[j] = 1, මම ලේඛයට ප්රතිඵල විය.

ඔබ බලාපොරොත්තු වන හැකි පරිදි, ආරම්භනය bහා dකම්බියක් ඇතුළත භාවිතා InitToZero[j]ඒකාබද්ධ ප්රවේශය වාසියක්, ඔවුන් සඳහා පැවරුම් පෙර නැවත-කිරීමට නැවත සිදු කරන ලදී ලෙස ලබා දුන් aසහ c, නමුත් තවමත් 10% ක් ඇතුළත. යන්න රූපය.

දෘඩාංග යනු ඩෙල් එක්ස්පීඑස් 8500 වන පරම්පරාවේ 3 කෝර් අයි 7 @ 3.4 ගිගාහර්ට්ස් සහ 8 ජීබී මතකයයි. 2 ^ 16 සිට 2 ^ 24 දක්වා, ලූප අටක් භාවිතා කරමින් සමුච්චිත කාලය පිළිවෙලින් 44.987 සහ 40.965 විය. දෘශ්‍ය සී ++ 2010, සම්පූර්ණයෙන්ම ප්‍රශස්ත කර ඇත.

PS: මම ලූප ශුන්‍යයට ගණනය කිරීම සඳහා වෙනස් කළ අතර ඒකාබද්ධ ක්‍රමය සුළු වශයෙන් වේගවත් විය. මගේ හිස සීරීමට. නව අරාව ප්‍රමාණ කිරීම සහ ලූප් ගණන් සටහන් කරන්න.

// MemBufferMystery.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <cmath>
#include <string>
#include <time.h>

#define  dbl    double
#define  MAX_ARRAY_SZ    262145    //16777216    // AKA (2^24)
#define  STEP_SZ           1024    //   65536    // AKA (2^16)

int _tmain(int argc, _TCHAR* argv[]) {
    long i, j, ArraySz = 0,  LoopKnt = 1024;
    time_t start, Cumulative_Combined = 0, Cumulative_Separate = 0;
    dbl *a = NULL, *b = NULL, *c = NULL, *d = NULL, *InitToOnes = NULL;

    a = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    b = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    c = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    d = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    InitToOnes = (dbl *)calloc( MAX_ARRAY_SZ, sizeof(dbl));
    // Initialize array to 1.0 second.
    for(j = 0; j< MAX_ARRAY_SZ; j++) {
        InitToOnes[j] = 1.0;
    }

    // Increase size of arrays and time
    for(ArraySz = STEP_SZ; ArraySz<MAX_ARRAY_SZ; ArraySz += STEP_SZ) {
        a = (dbl *)realloc(a, ArraySz * sizeof(dbl));
        b = (dbl *)realloc(b, ArraySz * sizeof(dbl));
        c = (dbl *)realloc(c, ArraySz * sizeof(dbl));
        d = (dbl *)realloc(d, ArraySz * sizeof(dbl));
        // Outside the timing loop, initialize
        // b and d arrays to 1.0 sec for consistent += performance.
        memcpy((void *)b, (void *)InitToOnes, ArraySz * sizeof(dbl));
        memcpy((void *)d, (void *)InitToOnes, ArraySz * sizeof(dbl));

        start = clock();
        for(i = LoopKnt; i; i--) {
            for(j = ArraySz; j; j--) {
                a[j] += b[j];
                c[j] += d[j];
            }
        }
        Cumulative_Combined += (clock()-start);
        printf("\n %6i miliseconds for combined array sizes %i and %i loops",
                (int)(clock()-start), ArraySz, LoopKnt);
        start = clock();
        for(i = LoopKnt; i; i--) {
            for(j = ArraySz; j; j--) {
                a[j] += b[j];
            }
            for(j = ArraySz; j; j--) {
                c[j] += d[j];
            }
        }
        Cumulative_Separate += (clock()-start);
        printf("\n %6i miliseconds for separate array sizes %i and %i loops \n",
                (int)(clock()-start), ArraySz, LoopKnt);
    }
    printf("\n Cumulative combined array processing took %10.3f seconds",
            (dbl)(Cumulative_Combined/(dbl)CLOCKS_PER_SEC));
    printf("\n Cumulative seperate array processing took %10.3f seconds",
        (dbl)(Cumulative_Separate/(dbl)CLOCKS_PER_SEC));
    getchar();

    free(a); free(b); free(c); free(d); free(InitToOnes);
    return 0;
}

MFLOPS අදාළ මෙට්‍රික් එකක් යැයි තීරණය කළේ ඇයිදැයි මට විශ්වාස නැත. මගේ අදහස වූයේ මතක ප්‍රවේශයන් කෙරෙහි අවධානය යොමු කිරීමයි, එබැවින් පාවෙන ලක්ෂ්‍ය ගණනය කිරීමේ කාලය අවම කිරීමට මම උත්සාහ කළෙමි. මම එහි ගියෙමි +=, නමුත් ඒ මන්දැයි මට විශ්වාස නැත.

ගණනය කිරීමක් නොමැති සෘජු පැවරුමක් මතක ප්‍රවේශ වේලාවේ පිරිසිදු පරීක්‍ෂණයක් වන අතර ලූප ගණන නොසලකා ඒකාකාරී වන පරීක්ෂණයක් නිර්මාණය කරයි. සමහර විට මට සංවාදයේ යමක් මග හැරී ඇති නමුත් එය ගැන දෙවරක් සිතීම වටී. ප්ලස් එක පැවරුමෙන් ඉවත් කර ඇත්නම්, සමුච්චිත කාලය තත්පර 31 බැගින් සමාන වේ.


1
ඔබ මෙහි සඳහන් කර ඇති අස්ථානගත කිරීමේ ද penalty ුවම නම් වැරදි ලෙස සකසන ලද තනි පැටවීමක් / ගබඩාවක් (නොබැඳි SSE භාරය / ගබඩා ඇතුළුව). නමුත් කාර්ය සාධනය විවිධ අරා වල සාපේක්ෂ පෙළගැස්මට සංවේදී බැවින් මෙහි තත්වය එසේ නොවේ. උපදෙස් මට්ටමින් අස්ථානගතවීම් නොමැත. සෑම බරක් / ගබඩාවක්ම නිසි ලෙස පෙළගස්වා ඇත.
ගුප්ත

19

එයට හේතුව CPU සතුව එතරම් හැඹිලි මිස් නොමැති වීමයි (එහිදී RAM චිප වලින් අරාව දත්ත පැමිණෙන තෙක් බලා සිටිය යුතුය). අරා වල ප්‍රමාණය අඛණ්ඩව වෙනස් කිරීම ඔබට සිත්ගන්නාසුළු වන අතර එමඟින් ඔබ 1 මට්ටමේ හැඹිලියේ (L1) ප්‍රමාණය ඉක්මවා , පසුව ඔබේ CPU හි 2 මට්ටමේ හැඹිලිය (L2) ඉක්මවා ඔබේ කේතය සඳහා ගතවන කාලය සැලසුම් කරන්න. අරා වල ප්‍රමාණයට එරෙහිව ක්‍රියාත්මක කිරීමට. ප්‍රස්ථාරය ඔබ බලාපොරොත්තු වන පරිදි සරල රේඛාවක් නොවිය යුතුය.


2
හැඹිලි ප්‍රමාණය සහ අරාව ප්‍රමාණය අතර කිසිදු අන්තර්ක්‍රියාකාරිත්වයක් ඇතැයි මම විශ්වාස නොකරමි. සෑම අරා මූලද්‍රව්‍යයක්ම භාවිතා කරනුයේ එක් වරක් පමණක් වන අතර පසුව ඒවා ආරක්ෂිතව ඉවත් කළ හැකිය. අරා හතරේ ගැටුමට එය හේතු වන්නේ නම්, හැඹිලි රේඛාවේ ප්‍රමාණය සහ අරාවෙහි ප්‍රමාණය අතර අන්තර්ක්‍රියාකාරිත්වයක් තිබිය හැකිය .
ඔලිවර් චාල්ස්වර්ත්

16

පළමු ලූපය එක් එක් විචල්යයේ ලිවීම ප්රත්ය කරයි. දෙවන හා තෙවන ඒවා සිදු කරන්නේ මූලද්‍රව්‍ය ප්‍රමාණයේ කුඩා පැනීම් පමණි.

සෙන්ටිමීටර 20 කින් වෙන් කරන ලද පෑනක් සහ කඩදාසි සමඟ කුරුස 20 ක සමාන්තර රේඛා දෙකක් ලිවීමට උත්සාහ කරන්න. එක් පේළියක් අවසන් කිරීමෙන් පසු අනෙක් පේළිය උත්සාහ කර එක් එක් පේළියේ කුරුසියක් විකල්ප ලෙස ලිවීමෙන් තවත් වරක් උත්සාහ කරන්න.


CPU උපදෙස් වැනි දේවල් ගැන සිතන විට සැබෑ ලෝක ක්‍රියාකාරකම්වල ප්‍රතිසමයන් අනතුරුදායක ය. ඔබ නිදර්ශනය කරන්නේ effectively ලදායී ලෙස කාලය සෙවීමයි , එය අපි භ්‍රමණය වන තැටියක ගබඩා කර ඇති දත්ත කියවීම / ලිවීම ගැන කතා කරන්නේ නම් අදාළ වේ, නමුත් CPU හැඹිලියේ (හෝ RAM හෝ SSD මත) සෙවීමේ කාලයක් නොමැත. මතකයේ නොගැලපෙන ප්‍රදේශවලට ප්‍රවේශ වීම කිසිදු ද penalty ුවමක් එදිරිව යාබද ප්‍රවේශයන්ට යටත් නොවේ.
FeRD

8

මුල් ප්‍රශ්නය

එක් ලූපයක් ලූප දෙකකට වඩා මන්දගාමී වන්නේ ඇයි?


නිගමනය:

නඩුව 1 යනු අකාර්යක්ෂම එකක් වන සම්භාව්‍ය අන්තර් මැදිහත්වීමේ ගැටලුවකි. බොහෝ යන්ත්‍ර සැකසුම් හා සංවර්ධකයින් බහු-නූල් යෙදුම් මෙන්ම සමාන්තර වැඩසටහන්කරණය කිරීමේ හැකියාව ඇති බහු-මූලික පද්ධති තැනීම හා සැලසුම් කිරීම අවසන් කිරීමට මෙය ප්‍රධාන හේතුවක් යැයි මම සිතමි.

RAM, හැඹිලි, පිටු ලිපිගොනු ආදිය සමඟ වැඩ කිරීම සම්බන්ධව ගොඩවල් වෙන් කිරීම සඳහා දෘඩාංග, මෙහෙයුම් පද්ධතිය සහ සම්පාදක (ය) එකට ක්‍රියා කරන ආකාරය සම්බන්ධ නොකර මේ ආකාරයේ ප්‍රවේශයකින් එය බැලීම; මෙම ඇල්ගොරිතමවල පදනමේ ඇති ගණිතය අපට පෙන්වන්නේ මෙම දෙකෙන් වඩා හොඳ විසඳුම කුමක්ද යන්නයි.

අපි ගැන සාදෘශ්යයක් භාවිතා කළ හැකි Bossවීම එය Summationනියෝජනය කරන බව For Loopසේවකයන් අතර ගමන් කිරීමට ඇති බව Aසහ B.

කේස් 2 අවම වශයෙන් අඩක් තරම් වේගවත් බව අපට පහසුවෙන් දැක ගත හැකිය. 1 වන නඩුවට වඩා මඳක් වැඩි නොවේ නම් ගමන් කිරීමට අවශ්‍ය දුර ප්‍රමාණය හා කම්කරුවන් අතර ගතවන කාලය. මෙම ගණිතය බෙන්ච්මාර්ක් ටයිම්ස් මෙන්ම එකලස් කිරීමේ උපදෙස්වල වෙනස්කම් ගණන සමඟ මුලුමනින්ම පාහේ හා පරිපූර්ණ ලෙස පෙළ ගැසී ඇත.


මේ සියල්ල පහතින් ක්‍රියාත්මක වන්නේ කෙසේදැයි මම දැන් පැහැදිලි කිරීමට පටන් ගනිමි.


ගැටලුව තක්සේරු කිරීම

OP හි කේතය:

const int n=100000;

for(int j=0;j<n;j++){
    a1[j] += b1[j];
    c1[j] += d1[j];
}

සහ

for(int j=0;j<n;j++){
    a1[j] += b1[j];
}
for(int j=0;j<n;j++){
    c1[j] += d1[j];
}

සලකා බැලීම

ලූප සඳහා වන ප්‍රභේද 2 පිළිබඳ OP හි මුල් ප්‍රශ්නය සහ හැඹිලි වල හැසිරීම පිළිබඳ ඔහුගේ සංශෝධිත ප්‍රශ්නය සහ තවත් බොහෝ විශිෂ්ට පිළිතුරු සහ ප්‍රයෝජනවත් අදහස් සලකා බැලීම; මෙම තත්වය සහ ගැටලුව පිළිබඳව වෙනස් ප්‍රවේශයක් ගැනීමෙන් මම මෙහි වෙනස් දෙයක් කිරීමට උත්සාහ කරමි.


ප්රවේශය

ලූප දෙක සහ හැඹිලි සහ පිටු ගොනු කිරීම පිළිබඳ සියලු සාකච්ඡා සලකා බැලීමේදී මෙය වෙනස් දෘෂ්ටිකෝණයකින් බැලීමට වෙනත් ප්‍රවේශයක් ගැනීමට මම කැමතියි. හැඹිලිය සහ පිටු ලිපිගොනු හෝ මතකය වෙන් කිරීම සඳහා ක්‍රියාත්මක කිරීම සම්බන්ධ නොවන එකක්, ඇත්ත වශයෙන්ම, මෙම ප්‍රවේශය සත්‍ය දෘඩාංග හෝ මෘදුකාංග ගැන කිසිසේත් තැකීමක් නොකරයි.


ඉදිරිදර්ශනය

ටික වේලාවක් කේතය දෙස බැලීමෙන් පසු ගැටලුව කුමක්ද සහ එය ජනනය කරන්නේ කුමක් ද යන්න පැහැදිලි විය. අපි මෙය ඇල්ගොරිතම ගැටළුවක් බවට පත් කර ගණිතමය අංකන භාවිතා කිරීමේ දෘෂ්ටි කෝණයෙන් බලමු. ඉන්පසු ගණිත ගැටලු මෙන්ම ඇල්ගොරිතම සඳහා ප්‍රතිසමයක් යොදමු.


අප දන්නා දේ

අපි දන්නවා මේ ලූපය 100,000 වතාවක් ධාවනය වන බව. ඒ වගේම අපි දන්නවා a1, b1, c1සහ d164-bit ගෘහ නිර්මාණ ශිල්පය පිළිබඳ සූචක ඇත. 32-බිට් යන්ත්‍රයක C ++ තුළ, සියලු දර්ශකයන් බයිට් 4 ක් වන අතර 64-බිට් 64 යන්ත්‍රයක, දර්ශකයන් ස්ථාවර දිගකින් යුක්ත බැවින් ඒවා ප්‍රමාණයෙන් බයිට් 8 කි.

මෙම අවස්ථා දෙකෙහිම වෙන් කළ යුතු බයිට් 32 ක් අප සතුව ඇති බව අපි දනිමු. එකම වෙනස වන්නේ අපි එක් එක් පුනරාවර්තනය සඳහා බයිට් 32 ක් හෝ බයිට් 2-8 කට්ටල 2 ක් වෙන් කිරීමයි. දෙවන අවස්ථාවෙහිදී අපි ස්වාධීන ලූප දෙකම සඳහා එක් එක් නැවතීමේ ක්‍රියාවලිය සඳහා බයිට් 16 ක් වෙන් කරමු.

ලූප දෙකම තවමත් මුළු ප්‍රතිපාදන වලින් බයිට් 32 ට සමාන වේ. මෙම තොරතුරු සමඟ අපි දැන් ඉදිරියට ගොස් මෙම සංකල්පවල සාමාන්‍ය ගණිතය, ඇල්ගොරිතම සහ ප්‍රතිසම පෙන්වමු.

අවස්ථා දෙකේදීම එකම කට්ටලයක් හෝ මෙහෙයුම් සමූහයක් සිදු කළ යුතු වාර ගණන අපි දනිමු. අවස්ථා දෙකේදීම වෙන් කළ යුතු මතක ප්‍රමාණය අපි දනිමු. මෙම අවස්ථා දෙකම අතර ප්‍රතිපාදනවල සමස්ත කාර්ය භාරය දළ වශයෙන් සමාන වනු ඇතැයි අපට තක්සේරු කළ හැකිය.


අප නොදන්නා දේ

අපි කවුන්ටරයක් ​​සකසා මිණුම් ලකුණක් පරීක්‍ෂා කළහොත් මිස එක් එක් සිද්ධිය සඳහා කොපමණ කාලයක් ගතවේදැයි අපි නොදනිමු. කෙසේ වෙතත්, මිණුම් සලකුණු දැනටමත් මුල් ප්‍රශ්නයෙන් සහ සමහර පිළිතුරු සහ අදහස් වලින් ඇතුළත් කර ඇත; මේ දෙක අතර සැලකිය යුතු වෙනසක් අපට දැකිය හැකි අතර මෙම ගැටලුවට මෙම යෝජනාවට සම්පූර්ණ හේතුව මෙයයි.


විමර්ශනය කරමු

ගොඩවල් වෙන් කිරීම, මිණුම් සලකුණු පරීක්ෂණ, RAM, හැඹිලි සහ පිටු ලිපිගොනු දෙස බැලීමෙන් බොහෝ දෙනෙක් දැනටමත් මෙය කර ඇති බව දැනටමත් පෙනේ. නිශ්චිත දත්ත ලක්ෂ්‍යයන් දෙස බැලීමේදී සහ නිශ්චිත පුනරාවර්තන දර්ශක ද ඇතුළත් කර ඇති අතර මෙම විශේෂිත ගැටළුව පිළිබඳ විවිධ සංවාදයන් බොහෝ දෙනෙකුට ඒ හා සම්බන්ධ වෙනත් කරුණු ප්‍රශ්න කිරීමට පටන් ගෙන තිබේ. ගණිතමය ඇල්ගොරිතම භාවිතා කරමින් එයට ප්‍රතිසමයක් යෙදීමෙන් අපි මෙම ගැටලුව දෙස බැලීමට පටන් ගන්නේ කෙසේද? අපි ප්‍රකාශ කිහිපයක් ඉදිරිපත් කිරීමෙන් ආරම්භ කරමු! ඉන්පසු අපි අපගේ ඇල්ගොරිතම එතැනින් ගොඩනගා ගනිමු.


අපගේ ප්‍රකාශ:

  • අපගේ ලූපය සහ එහි පුනරාවර්තනයන් 1 සිට ආරම්භ කර 100000 කින් අවසන් වන අතර එය ලූපවල මෙන් 0 සමඟ ආරම්භ වනවා සේම අපි මතක තබා ගත යුතු බැවින් මතක ඇමතීමේ 0 සුචිගත කිරීමේ යෝජනා ක්‍රමය ගැන කරදර විය යුතු නැත. ඇල්ගොරිතම ම ය.
  • මෙම අවස්ථා දෙකෙහිම අපට වැඩ කිරීමට කාර්යයන් 4 ක් සහ ක්‍රියාකාරී ඇමතුම් 2 ක් ඇත. අප පහත සඳහන් ලෙස කටයුතු කිරීමට ඇති කාර්යයන් හා ඇමතුම් ලෙස මෙම සකස් කරනු ඇත: F1(), F2(), f(a), f(b), f(c)හා f(d).

ඇල්ගොරිතම:

1 වන අවස්ථාව: - එක් සාරාංශයක් පමණක් නොව ස්වාධීන ශ්‍රිත ඇමතුම් දෙකක් පමණි.

Sum n=1 : [1,100000] = F1(), F2();
                       F1() = { f(a) = f(a) + f(b); }
                       F2() = { f(c) = f(c) + f(d); }

2 වන අවස්ථාව: - සාරාංශ දෙකක් නමුත් සෑම එකක්ම තමන්ගේම ක්‍රියාකාරී ඇමතුමක් ඇත.

Sum1 n=1 : [1,100000] = F1();
                        F1() = { f(a) = f(a) + f(b); }

Sum2 n=1 : [1,100000] = F1();
                        F1() = { f(c) = f(c) + f(d); }

ඔබ දැක නම් F2()පමණක් පවතී Sumසිට Case1එහිදී F1()අඩංගු Sumසිට Case1හා දෙකේම Sum1හා Sum2සිට Case2. දෙවන ඇල්ගොරිතම තුළ සිදුවන ප්‍රශස්තිකරණයක් ඇති බව අප නිගමනය කිරීමට පටන් ගත් විට මෙය පසුව පැහැදිලි වනු ඇත.

පළමු සිද්ධි Sumඇමතුම් හරහා පුනරාවර්තනයන් f(a)එහි ආත්මයට එකතු වන අතර f(b)පසුව එය ඇමතුම f(c)එකම දේ කරන නමුත් එක් f(d)එක් 100000පුනරාවර්තන සඳහා එයම එකතු කරයි . දෙවන අවස්ථාවෙහිදී, අප සතුව ඇති Sum1අතර Sum2දෙකම එක හා සමානව ක්‍රියා කරන අතර ඒවා එකම ශ්‍රිතයක් ලෙස දෙවරක් කැඳවනු ලැබේ.

මෙම අවස්ථාවේ දී අප ප්රතිකාර කළ හැක Sum1සහ Sum2පමණක් සරල පැරණි ලෙස Sumඑහිදී Sumමේ වගේ මෙම නඩුවේ පෙනුම: Sum n=1 : [1,100000] { f(a) = f(a) + f(b); }අපි හිතුවා ඒ එකම කාර්යය විය සලකා කළ හැකි උපරිම ඵල නෙළා වැනි දැන් මෙම පෙනුම.


ප්‍රතිසම සමඟ සාරාංශය

දෙවන අවස්ථාවෙහිදී අප දුටු දෙයින් පෙනෙන්නේ ලූප සඳහා දෙකම එකම නිශ්චිත අත්සනක් ඇති බැවින් ප්‍රශස්තිකරණයක් ඇති බවයි. නමුත් මෙය සැබෑ ගැටළුව නොවේ. ප්රශ්නය විසින් සිදු කරන ලැබ ඇත ක්රියාකාරකම් නොවේ f(a), f(b), f(c), සහ f(d). අවස්ථා දෙකේදීම සහ දෙක අතර සංසන්දනය, එය එක් එක් අවස්ථාවෙහිදී සාරාංශය ගමන් කළ යුතු දුරෙහි වෙනස වන අතර එය ක්‍රියාත්මක කිරීමේ වේලාවේ වෙනස ඔබට ලබා දෙයි.

ඒ පිළිබඳව ද For Loopsමෙම ලෙස Summationsඑය ලෙස ඒ අනුකරණ කරන්නේ බව Bossදෙදෙනෙක් නියෝග ලබා දෙන Aසහ Bඔවුන්ගේ රැකියා මස් බව Cසහ Dපිළිවෙළින් සහ ඔවුන්ගෙන් සමහර පැකේජය තෝරාගන්න එය නැවත එන්න. මෙම ප්‍රතිසමයේ දී, ලූප හෝ සාරාංශ පුනරාවර්තන සහ තත්ව පරීක්ෂාවන් සඳහා සත්‍ය වශයෙන්ම නියෝජනය නොවේ Boss. දේ ඇත්තටම නියෝජනය Bossසෘජුවම සැබෑ ගණිතමය ගණිත ක්රමයක් නොවන නමුත් සැබෑ සංකල්පය සිට Scopeහා Code Blockපුරුද්දක් හෝ subroutine තුළ, ක්රමය, කර්තව්යයක්, පරිවර්තන ඒකකය, ආදිය පළමු ඇල්ගොරිතමය 2 වන ඇල්ගොරිතම අඛණ්ඩව විෂය පථ 2 වේ එහිදී 1 විෂය පථය වේ.

එක් එක් ඇමතුමක් ස්ලිප් මත පළමු අවස්ථාවේදී ඇතුලත, Bossයයි Aද නම් ලබා දෙන හා Aලබා ගැනීමට ඇති වෙනවා B'sපැකේජය පසුව Bossයයි Cද නියෝග එසේ සිට පැකේජය ලබා ගැනීමට ලබා දෙන Dඑක් එක් ප්රතිඵලය මත.

දෙවන අවස්ථාව තුළ, සියලු පැකේජ ලැබෙන තුරු පැකේජය ලබා ගැනීමට Bossකෙලින්ම ක්‍රියා කරයි . එවිට සියලු පැකේජ ලබා ගැනීම සඳහා එකම දේ කිරීමට කටයුතු කරයි .AB'sBossCD's

අපි බයිට් 8 ක දර්ශකයක් සමඟ වැඩ කරන අතර ගොඩවල් වෙන් කිරීම සමඟ කටයුතු කරන බැවින් පහත සඳහන් ගැටලුව සලකා බලමු. බව අපි එකතු කියා Bossසිට අඩි 100 Aහා Aඅඩි 500 ක් වේ C. මරණ ද of ුවම නියම වීම නිසා Bossමුලදී එය කොතරම් දුරද යන්න ගැන අපට කරදර විය යුතු නැත C. අවස්ථා දෙකේදීම, Bossමුලින් ගමන් කරන්නේ මුල සිට Aඑතැන් සිටය B. මෙම ප්‍රතිසමය මෙම දුර නිශ්චිත යැයි කීම නොවේ; එය ඇල්ගොරිතම වල ක්‍රියාකාරිත්වය පෙන්වීමට ප්‍රයෝජනවත් පරීක්ෂණ අවස්ථාවකි.

බොහෝ අවස්ථාවන්හීදී, ගොඩවල් වෙන් කිරීම සහ හැඹිලි සහ පිටු ලිපිගොනු සමඟ වැඩ කිරීමේදී, ලිපින ස්ථාන අතර මෙම දුර ප්‍රමාණය එතරම් වෙනස් නොවිය හැකිය හෝ දත්ත වර්ගවල ස්වභාවය සහ අරාව ප්‍රමාණ අනුව ඒවා සැලකිය යුතු ලෙස වෙනස් විය හැකිය.


පරීක්ෂණ අවස්ථා:

පළමු නඩු: පළමු ප්රතිඵලය මත මෙමBossමුලින් නියෝග ස්ලිප් දෙන්න අඩි 100 යෑමට සිදු වී ඇතිAසහAලකුණු ගොස් ඔහුගේ දෙයක් කරනවා, ඒත් දැන්Bossඅඩි 500 ක් දක්වා ගමන් කිරීමට ඇතිCඔහු තම නියෝගය ස්ලිප් දෙන්න. ඊළඟ පුනරාවර්තනය සහ අනෙක් සෑමBossනැවත නැවත සිදුකිරීමේදී දෙදෙනා අතර අඩි 500 ක් ඉදිරියට හා පසුපසට යා යුතුය.

දෙවන නඩු: මෙමBossපළමු ප්රතිඵලය මත අඩි 100 ගමන් කිරීමට ඇතිA, නමුත් ඊට පස්සේ, ඔහු එහි දැනටමත් සඳහා පමණක් ඔබා එයAසියලු ස්ලිප් පිරී තෙක් නැවත ලබා ගැනීමට. එවිටBossපළමු ප්රතිඵලය මත අඩි 500 ගමන් කිරීමට ඇතිCනිසාCසිට අඩි 500 ක් වේA. මෙයඔහුBoss( Summation, For Loop )සමඟ වැඩ කිරීමෙන් පසු වහාම කැඳවනු ලබනබැවින්,ඇණවුම් ස්ලිප් සියල්ල සිදු වනතුරුAඔහු කළ ආකාරයටම ඔහු එහිAරැඳීC'sසිටී.


ගමන් කළ දුරවල වෙනස

const n = 100000
distTraveledOfFirst = (100 + 500) + ((n-1)*(500 + 500); 
// Simplify
distTraveledOfFirst = 600 + (99999*100);
distTraveledOfFirst = 600 + 9999900;
distTraveledOfFirst =  10000500;
// Distance Traveled On First Algorithm = 10,000,500ft

distTraveledOfSecond = 100 + 500 = 600;
// Distance Traveled On Second Algorithm = 600ft;    

අත්තනෝමතික වටිනාකම් සංසන්දනය කිරීම

600 ක් මිලියන 10 ට වඩා බෙහෙවින් අඩු බව අපට පහසුවෙන් දැක ගත හැකිය. දැන්, මෙය හරියටම නොවේ, මන්ද යත්, RAM හි ලිපිනය හෝ හැඹිලිය හෝ පිටු ගොනුව අතර ඇති දුරෙහි සැබෑ වෙනස අප නොදන්නා නිසා එක් එක් නැවතීමේ සෑම ඇමතුමක්ම නොපෙනෙන බොහෝ විචල්‍යයන් නිසා සිදුවනු ඇත. මෙය තත්වය පිළිබඳව දැනුවත්ව සිටීම හා නරකම අවස්ථාව දෙස බැලීම පමණි.

මෙම සංඛ්‍යා වලින් එය බොහෝ දුරට පෙනෙනුයේ ඇල්ගොරිතම 1 99%ඇල්ගොරිතම දෙකට වඩා මන්දගාමී විය යුතුය ; කෙසේ වෙතත්, මෙය Boss'sඇල්ගොරිතම වල කොටසක් හෝ වගකීමක් පමණක් වන අතර එය සැබෑ කම්කරුවන් A, Bසහ C, සහ Dලූපයේ සෑම ක්‍රියාවලියක් සඳහාම ඔවුන් කළ යුතු දේ පිළිබඳව ගණන් නොගනී . එබැවින් ලොක්කාගේ රැකියාව සිදු කරනු ලබන්නේ සම්පූර්ණ වැඩ වලින් 15 - 40% ක් පමණි. කම්කරුවන් හරහා සිදු කරනු ලබන කාර්යයන්ගෙන් වැඩි ප්‍රමාණයක් වේග අනුපාත වෙනස 50-70% දක්වා තබා ගැනීම කෙරෙහි තරමක් විශාල බලපෑමක් ඇති කරයි


නිරීක්ෂණය: - ඇල්ගොරිතම දෙක අතර වෙනස්කම්

මෙම තත්වය තුළ, එය සිදු කරනු ලබන කාර්යයේ ව්යුහය වේ. සමාන ක්‍රියාකාරී ප්‍රකාශයක් සහ අර්ථ දැක්වීමක් ඇති අර්ධ ප්‍රශස්තිකරණයෙන් කේස් 2 වඩාත් කාර්යක්ෂම බව පෙන්වීමට එය යන්නේ නම සහ ගමන් කළ දුර අනුව වෙනස් වන විචල්‍යයන් පමණි.

ඒ වගේම අපි ගමන් මුළු දුර බව දකින්න නඩු 1 වඩා තවත් එහි ඇති වඩා නඩු 2 අපි මේ දුර, අපේ ගමන් සලකා බැලිය හැකි කාල සාධකය වන ගණිත ක්රමයක් දෙක අතර. නඩුව 1 ට වඩා කරන්න සැලකිය යුතු වැඩි කරන වැඩ නඩුව 2 කරන්නේ.

මෙම ASMඅවස්ථා දෙකෙහිම පෙන්වා ඇති උපදෙස් වල සාක්ෂි වලින් මෙය නිරීක්ෂණය කළ හැකිය . මෙම නඩු ගැන දැනටමත් සඳහන් දේ සමග, මේ බව සඳහා වැයවේ නැත නඩු 1 ලොක්කා යන දෙකම සඳහා බලා සිටීමට සිදු වනු ඇත Aසහ Cඔහු ආපසු ගමන් කිරීමට පෙර ආපසු ලබා ගැනීමට Aසේම සෑම සඳහා නැවත. අතිශය දීර් time කාලයක් ගත වුවහොත් Aහෝ අනෙක් සේවකයා (ව) ක්‍රියාත්මක කිරීමට අක්‍රියව සිටින බවට ද එය ගණන් නොගනී .BBoss

දී නඩු 2 එකම එක් දැරියක අලස වේ Bossකම්කරුවා නැවත පැමිණෙන තුරු. එබැවින් මෙය පවා ඇල්ගොරිතම කෙරෙහි බලපෑමක් ඇති කරයි.



OPs සංශෝධිත ප්‍රශ්නය (ය)

සංස්කරණය කරන්න: හැසිරීම අරා (n) සහ CPU හැඹිලියේ ප්‍රමාණයන් මත දැඩි ලෙස රඳා පවතින බැවින් ප්‍රශ්නය කිසිදු අදාල නොවේ. එබැවින් වැඩි උනන්දුවක් ඇත්නම්, මම ප්‍රශ්නය නැවත ලියමි:

පහත දැක්වෙන ප්‍රස්ථාරයේ කලාප පහ විසින් නිරූපණය කර ඇති පරිදි විවිධ හැඹිලි හැසිරීම් වලට තුඩු දෙන විස්තර පිළිබඳව ඔබට යම් අවබෝධයක් ලබා දිය හැකිද?

මෙම CPU සඳහා සමාන ප්‍රස්ථාරයක් ලබා දීමෙන් CPU / හැඹිලි ගෘහ නිර්මාණ ශිල්පය අතර ඇති වෙනස්කම් පෙන්වා දීම සිත්ගන්නා සුළු විය හැකිය.


මෙම ප්‍රශ්න සම්බන්ධයෙන්

මා සැකයකින් තොරව නිරූපණය කර ඇති පරිදි, දෘඩාංග හා මෘදුකාංග සම්බන්ධ වීමට පෙර පවා යටින් පවතින ගැටලුවක් තිබේ.

පිටු ලිපිගොනු ආදිය සමඟ මතකය සහ හැඹිලිය කළමනාකරණය කිරීම සඳහා පහත සඳහන් කරුණු අතර ඒකාබද්ධ පද්ධති සමූහයක් තුළ සියල්ල එකට වැඩ කරයි:

  • The Architecture {දෘඩාංග, ස්ථිරාංග, කාවැද්දූ ධාවක, කර්නල් සහ ASM උපදෙස් කට්ටල}.
  • The OS{ගොනු සහ මතක කළමනාකරණ පද්ධති, රියදුරන් සහ ලේඛකාධිකාරය}.
  • The Compiler Code පරිවර්තන ඒකක සහ ප්‍රභව කේතයේ ප්‍රශස්තිකරණය}.
  • Source Codeසුවිශේෂී ඇල්ගොරිතම සමූහයක් සමඟ පවා .

අපි පවා අත්තනෝමතික සමග කිසිම යන්ත්රයක් එය අදාළ පෙර පළමු ඇල්ගොරිතමය තුළ සිදු වන බව භාධකයක් ඇති බව අප දැනටමත් දැක ගැනීමට හැකි වන Architecture, OSහා Programmable Languageදෙවන ඇල්ගොරිතමය සාපේක්ෂව. නවීන පරිගණකයක සහජයෙන්ම සම්බන්ධ වීමට පෙර දැනටමත් ගැටලුවක් තිබේ.


අවසන් ප්‍රති .ල

කෙසේවෙතත්; මෙම නව ප්‍රශ්න වැදගත් නොවන බව කීම නොවේ, මන්ද ඒවා ඔවුන්ම වන අතර ඒවා සියල්ලටම පසුව කාර්යභාරයක් ඉටු කරයි. ඒවා ක්‍රියාපටිපාටියට හා සමස්ත ක්‍රියාකාරිත්වයට බලපෑම් කරන අතර ඔවුන්ගේ පිළිතුරු (පිළිතුරු) සහ අදහස් දැක්වීම් ලබා දී ඇති බොහෝ අයගේ විවිධ ප්‍රස්ථාර සහ ඇගයීම්වලින් එය පැහැදිලි වේ.

ඔබ පිළිබඳ නිදර්ශනය අවධානය යොමු නම් Bossසහ සේවකයන් දෙදෙනෙක් Aසහ Bකරන ඇසුරුම් ගොස් යළි ලබාගැනීම සඳහා ඇති Cසහ Dපිළිවෙළින් ප්රශ්නයට ගණිත ක්රමයක් දෙකක් ගණිතමය අංකන සලකා; පරිගණක දෘඩාංග සම්බන්ධ නොවී ඔබට දැකිය හැකි අතර මෘදුකාංගය Case 2ආසන්න වශයෙන් 60%වඩා වේගවත් වේ Case 1.

මෙම ඇල්ගොරිතම කිසියම් ප්‍රභව කේතයකට යොදන ලද පසු, ප්‍රස්ථාර සහ ප්‍රස්ථාර දෙස බැලූ විට, ලබා දී ඇති දෘඩාංග කැබැල්ලක ක්‍රියාකාරිත්වය සිදු කිරීම සඳහා මෙහෙයුම් පද්ධතිය හරහා සම්පාදනය කර, ප්‍රශස්තිකරණය කර ක්‍රියාත්මක කළ විට, වෙනස්කම් අතර තව ටිකක් පිරිහීමක් ඔබට දැකිය හැකිය. මෙම ඇල්ගොරිතම වල.

නම් Dataකට්ටලයක් තරමක් කුඩා වන එය පළමු වෙනසක් සියලු නරක දෙයක් විය හැක. කෙසේ වෙතත්, පසුව Case 1ක් පමණ වන 60 - 70%මන්දගාමී වඩා Case 2අපි කාලය මරණ දඬුවම් පැමිණවීමෙහි වෙනස්කම් අනුව මෙම ශ්රිතය වර්ධනය දෙස බලා සිටිය හැක:

DeltaTimeDifference approximately = Loop1(time) - Loop2(time)
//where 
Loop1(time) = Loop2(time) + (Loop2(time)*[0.6,0.7]) // approximately
// So when we substitute this back into the difference equation we end up with 
DeltaTimeDifference approximately = (Loop2(time) + (Loop2(time)*[0.6,0.7])) - Loop2(time)
// And finally we can simplify this to
DeltaTimeDifference approximately = [0.6,0.7]*Loop2(time)

මෙම දළ විශ්ලේෂණය ඇල්ගොරිතම සහ මෘදුකාංග ප්‍රශස්තිකරණය සහ යන්ත්‍ර උපදෙස් සම්බන්ධ යන්ත්‍ර මෙහෙයුම් අතර මෙම ලූප දෙක අතර සාමාන්‍ය වෙනසයි.

දත්ත කට්ටලය රේඛීයව වර්ධනය වන විට, දෙක අතර කාලයෙහි වෙනස ද එසේමය. ඇල්ගොරිතමය 1 ඇල්ගොරිතමය 2 ට වඩා වැඩි මිලක් ඇති වූ විට පැහැදිලි වන Bossසංචාරක සහ පසුපසට අතර උපරිම දුර යුතු Aසහ Cපළමු ප්රතිඵලයක්ම පසු සෑම ප්රතිඵලයක්ම සඳහා ඇල්ගොරිතම 2 අතර, Bossගමන් කිරීමට ඇති Aඑක් වරක් හා පසුව සිදු කිරීමෙන් පසු Aඔහු ගමන් කිරීමට ඇති සිට යන විට එකම එක කාලය උපරිම දුර Aකිරීමට C.

Bossඑක හා සමාන කාර්යයන් දෙකක් එකවර සිදු කිරීම කෙරෙහි අවධානය යොමු කිරීමට උත්සාහ කිරීම හා ඒ හා සමාන අඛණ්ඩ කාර්යයන් කෙරෙහි අවධානය යොමු කරනවා වෙනුවට ඒවා ඉදිරියට හා පසුපසට තල්ලු කිරීම, ඔහුට සංචාරය කිරීමට හා දෙගුණයක් වැඩ කිරීමට සිදු වූ බැවින් දවස අවසානයේදී ඔහු තරමක් කෝපයට පත් වනු ඇත. එම නිසා ලොක්කාගේ සහකරු හෝ සහකාරිය සහ දරුවන් එය අගය නොකරන හෙයින් ඔබේ ලොක්කාට අන්තර් සම්බන්ධිත බාධකයකට පැටලීමෙන් තත්වයේ විෂය පථය අහිමි නොකරන්න.



සංශෝධනය: මෘදුකාංග ඉංජිනේරු සැලසුම් මූලධර්ම

- ලූප සඳහා පුනරාවර්තනය තුළ ඇති ගණනය කිරීම් අතර වෙනස Local Stackසහ Heap Allocatedඒවායේ භාවිතයන්, ඒවායේ කාර්යක්ෂමතාව සහ effectiveness ලදායීතාවය අතර වෙනස -

මා ඉහත යෝජනා කළ ගණිතමය ඇල්ගොරිතම ප්‍රධාන වශයෙන් අදාළ වන්නේ ගොඩවල් මත වෙන් කර ඇති දත්ත මත මෙහෙයුම් සිදුකරන ලූපවලට ය.

  • අඛණ්ඩ තොග මෙහෙයුම්:
    • ලූප තනි දත්ත කේතයක් තුළ හෝ තොග රාමුව තුළ ඇති දත්ත මත දේශීයව මෙහෙයුම් සිදු කරන්නේ නම්, එය තවමත් අදාළ වේ, නමුත් මතක ස්ථාන ඒවා සාමාන්‍යයෙන් අනුක්‍රමික වන ස්ථානයට වඩා සමීප වන අතර දුර ගමන් හෝ ක්‍රියාත්මක කිරීමේ වේලාවේ වෙනස බොහෝ දුරට නොසැලකිලිමත් ය. ගොඩවල් තුළ කිසිදු ප්‍රතිපාදනයක් සිදු නොවන බැවින්, මතකය විසිරී නොයන අතර, මතකය බැටළුවා හරහා ලබා නොගනී. මතකය සාමාන්‍යයෙන් අනුක්‍රමික වන අතර තොග රාමුවට හා තොග දර්ශකයට සාපේක්ෂව වේ.
    • තොගයේ අඛණ්ඩව මෙහෙයුම් සිදුකරන විට, නවීන ප්‍රොසෙසරයක් මඟින් පුනරාවර්තන අගයන් සහ දේශීය හැඹිලි ලේඛනයේ මෙම අගයන් රඳවා තබා ගනී. මෙහි මෙහෙයුම් හෝ උපදෙස් කාලය නැනෝ තත්පර අනුපිළිවෙල අනුව වේ.
  • අඛණ්ඩ ගොඩවල් වෙන් කළ මෙහෙයුම්:
    • ඔබ ගොඩවල් වෙන්කිරීම් ආරම්භ කරන විට සහ ප්‍රොසෙසරයට CPU, බස් පාලක සහ රාම් මොඩියුලවල ගෘහ නිර්මාණ ශිල්පය මත පදනම්ව අඛණ්ඩ ඇමතුම් මගින් මතක ලිපින ලබා ගත යුතුය. මෙහෙයුම් හෝ ක්‍රියාත්මක කිරීමේ කාලය මයික්‍රෝ සිට අනුපිළිවෙලට විය හැකිය. මිලි තත්පර. හැඹිලි තොග මෙහෙයුම් වලට සාපේක්ෂව මේවා තරමක් මන්දගාමී ය.
    • CPU විසින් රාම් වෙතින් මතක ලිපිනය ලබා ගත යුතු අතර සාමාන්‍යයෙන් පද්ධති බස් රථය හරහා ඇති ඕනෑම දෙයක් CPU තුළ ඇති අභ්‍යන්තර දත්ත මාර්ග හෝ දත්ත බස් වලට සාපේක්ෂව මන්දගාමී වේ.

එබැවින් ඔබ ගොඩවල් මත තිබිය යුතු දත්ත සමඟ වැඩ කරන විට සහ ඔබ ඒවා හරහා ලූප හරහා ගමන් කරන විට, එක් එක් දත්ත කට්ටලය සහ ඊට අනුරූප ඇල්ගොරිතම එහි තනි පුඩුවක් තුළ තබා ගැනීම වඩාත් කාර්යක්ෂම වේ. ගොඩවල් මත ඇති විවිධ දත්ත කට්ටලවල බහුවිධ මෙහෙයුම් තනි පුඩුවක් තුළට දැමීමෙන් අඛණ්ඩ ලූප සාධක කිරීමට උත්සාහ කිරීම හා සසඳන විට ඔබට වඩා හොඳ ප්‍රශස්තිකරණයක් ලැබෙනු ඇත.

ඒවා නිතරම හැඹිලිගත කර ඇති බැවින් තොගයේ ඇති දත්ත සමඟ මෙය කිරීම කමක් නැත, නමුත් එහි මතක ලිපිනය තිබිය යුතු දත්ත සඳහා සෑම පුනරාවර්තනයක්ම විමසා බැලිය යුතුය.

මෘදුකාංග ඉංජිනේරු සහ මෘදුකාංග ගෘහ නිර්මාණ සැලසුම් ක්‍රියාත්මක වන්නේ මෙහිදීය. එය ඔබගේ දත්ත සංවිධානය කරන්නේ කෙසේදැයි දැන ගැනීමට ඇති හැකියාව, ඔබේ දත්ත හැඹිලිගත කරන්නේ කවදාදැයි දැන ගැනීම, ඔබේ දත්ත ගොඩවල් මත වෙන් කළ යුත්තේ කවදාදැයි දැන ගැනීම, ඔබේ ඇල්ගොරිතම සැලසුම් කර ක්‍රියාත්මක කරන්නේ කෙසේදැයි දැන ගැනීම සහ ඒවා අමතන්නේ කවදාද සහ කොතැනද යන්න දැන ගැනීමයි.

එකම දත්ත කට්ටලයට අදාළ ඇල්ගොරිතම ඔබ සතුව තිබිය හැකි නමුත්, O(n)වැඩ කරන විට ඇල්ගොරිතමයේ සංකීර්ණතාවයෙන් දැකිය හැකි ඉහත ගැටලුව නිසාම, එහි කොටස් ප්‍රභේදය සඳහා එක් ක්‍රියාත්මක කිරීමේ සැලසුමක් සහ තවත් ගොඩවල් වෙන් කරන ලද ප්‍රභේදය සඳහා ඔබට අවශ්‍ය විය හැකිය. ගොඩවල් සමඟ.

වසර ගණනාවක් තිස්සේ මා දුටු දෙයින් බොහෝ දෙනෙක් මෙම කාරණය සැලකිල්ලට නොගනිති. ඔවුන් විශේෂිත දත්ත කට්ටලයක් මත ක්‍රියා කරන එක් ඇල්ගොරිතමයක් සැලසුම් කිරීමට නැඹුරු වන අතර දත්ත කට්ටලය තොගයේ දේශීයව හැඹිලිගත කර හෝ එය ගොඩවල් මත වෙන් කර ඇත්ද යන්න නොසලකා ඔවුන් එය භාවිතා කරනු ඇත.

ඔබට සත්‍ය ප්‍රශස්තිකරණය අවශ්‍ය නම්, ඔව් එය කේත අනුපිටපතක් සේ පෙනේ, නමුත් සාමාන්‍යකරණය කිරීම සඳහා එකම ඇල්ගොරිතමයේ ප්‍රභේද දෙකක් තිබීම වඩාත් කාර්යක්ෂම වනු ඇත. එකක් තොග මෙහෙයුම් සඳහා වන අතර අනෙක පුනරාවර්තන ලූපවල සිදු කරන ගොඩවල් මෙහෙයුම් සඳහා!

මෙන්න ව්‍යාජ උදාහරණයක්: සරල ව්‍යුහ දෙකක්, එක් ඇල්ගොරිතමයක්.

struct A {
    int data;
    A() : data{0}{}
    A(int a) : data{a}{} 
};
struct B {
    int data;
    B() : data{0}{}
    A(int b) : data{b}{}
}                

template<typename T>
void Foo( T& t ) {
    // do something with t
}

// some looping operation: first stack then heap.

// stack data:
A dataSetA[10] = {};
B dataSetB[10] = {};

// For stack operations this is okay and efficient
for (int i = 0; i < 10; i++ ) {
   Foo(dataSetA[i]);
   Foo(dataSetB[i]);
}

// If the above two were on the heap then performing
// the same algorithm to both within the same loop
// will create that bottleneck
A* dataSetA = new [] A();
B* dataSetB = new [] B();
for ( int i = 0; i < 10; i++ ) {
    Foo(dataSetA[i]); // dataSetA is on the heap here
    Foo(dataSetB[i]); // dataSetB is on the heap here
} // this will be inefficient.

// To improve the efficiency above, put them into separate loops... 

for (int i = 0; i < 10; i++ ) {
    Foo(dataSetA[i]);
}
for (int i = 0; i < 10; i++ ) {
    Foo(dataSetB[i]);
}
// This will be much more efficient than above.
// The code isn't perfect syntax, it's only psuedo code
// to illustrate a point.

ගොඩවල් ප්‍රභේදවලට එදිරිව තොග ප්‍රභේද සඳහා වෙනම ක්‍රියාත්මක කිරීම මගින් මා සඳහන් කළේ මෙයයි. ඇල්ගොරිතමයන්ම එතරම් වැදගත් නැත, එය ඔබ ඒවා භාවිතා කරන ලූප ව්‍යුහයන් වේ.


මම මෙම පිළිතුර පළ කර ටික කලක් ගතවී ඇත, නමුත් මෙය තේරුම් ගැනීමට උපකාරී වන ඉක්මන් අදහස් දැක්වීමක් කිරීමට ද මට අවශ්‍ය විය: ලොක්කා සඳහා ලොක්කා සමඟ මගේ ප්‍රතිසමයට අනුව හෝ ලූපයක් හරහා සාරාංශ හෝ පුනරාවර්තන, අපට ද හැකිය මෙම ලොක්කා ලූප සඳහා විෂය පථය සහ සිරස් විචල්‍යයන් සහ මතක ආමන්ත්‍රණය කළමනාකරණය කරන ස්ටැක් ෆ්‍රේම් සහ ස්ටැක් පොයින්ටර් අතර සංයෝජනය ලෙස සලකන්න.
ෆ්‍රැන්සිස් කූග්ලර්

EtPeterMortensen මගේ මුල් පිළිතුර තරමක් වෙනස් කිරීමෙන් මම ඔබේ උපදෙස් සැලකිල්ලට ගත්තා. මම විශ්වාස කරන්නේ මෙය ඔබ යෝජනා කළ බවයි.
ෆ්‍රැන්සිස් කූග්ලර්

2

එය පැරණි C ++ සහ ප්‍රශස්තිකරණය විය හැකිය. මගේ පරිගණකයේ මම එකම වේගයක් ලබා ගත්තා:

එක් පුඩුවක්: 1.577 ms

ලූප දෙකක්: 1.507 ms

මම විෂුවල් ස්ටුඩියෝ 2015 ධාවනය කරන්නේ 16 GB RAM සහිත E5-1620 3.5 GHz ප්‍රොසෙසරයක ය.

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.