Kodėl atskiruose cikluose stigma papildai yra daug greičiau nei kombinuotame cikle?

Tarkime, kad a1 , b1 , c1 ir d1 nukreipia į atmintį, o mano skaitmeninis kodas turi tokią pagrindinę kilpą.

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

Ši kilpa vykdoma per 10000 kartų per kitą išorinę kilpą. Kad pagreitintumėte, pakeitiau kodą į:

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

Sudaryta MS Visual C ++ 10.0 su pilnu optimizavimu ir „ SSE2“ įgalinta 32 bitų „ Intel Core 2 Duo“ (x64), pirmasis pavyzdys trunka 5,5 sekundės, o pavyzdys su dviguba kilpa trunka tik 1,9 sekundės. Mano klausimas yra toks: (žr. Toliau pateiktą mano pakeitimo klausimą)

PS: nesu tikras, ar tai padės:

Pirmojo ciklo išmontavimas iš esmės atrodo taip (šis blokas kartojamas maždaug penkis kartus per visą programą):

 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] 

Kiekvienas dvigubos kilpos pavyzdžio kilpas sukuria šį kodą (šis blokas kartojamas maždaug tris kartus):

 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 

Klausimas pasirodė esąs nesvarbus, nes elgesys labai priklauso nuo masyvų dydžio (n) ir CPU talpyklos. Taigi, jei yra daugiau interesų, aš perrašau klausimą:

Ar galėtumėte pateikti išsamią informaciją apie detales, dėl kurių atsiranda skirtingas talpyklos elgesys, kaip parodyta penkiose toliau pateikto grafiko srityse?

Taip pat būtų įdomu atkreipti dėmesį į skirtumus tarp CPU ir talpyklos architektūrų, pateikiant panašų šių procesorių tvarkaraštį.

PPS: čia yra visas kodas. Ji naudoja TBB Tick_Count kad sinchronizuotų didesnę skiriamąją gebą, kuri gali būti išjungta nenurodant 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; } } 

(Rodo FLOP / s skirtingoms n reikšmėms.)

2019

2073 m
17 дек. Johannes Gerer gruodis 17 d. 2011-12-17 23:40 '11, 23:40, 2011-12-17 23:40
@ 10 atsakymų

Išsamiau išnagrinėjus tai, manau, kad tai (bent jau iš dalies) yra dėl keturių rodiklių suderinimo. Tai sukels tam tikrą talpyklos / kelio konfliktą.

Jei teisingai suprantu, kaip paskirstote savo masyvus, jie greičiausiai bus suderinti su puslapio eilute .

Tai reiškia, kad visi skambučiai kiekviename cikle pateks į tą patį talpyklos failą. Tačiau „Intel“ procesoriai tam tikrą laiką turėjo 8 krypčių L1 talpyklos asociatyvumą. Bet iš tikrųjų spektaklis nėra visiškai vienodas. Prieiga prie 4 kanalų kanalų vis dar yra lėtesnė nei dvipusis.

EDIT: Iš tikrųjų atrodo, kad pasirinksite visas matricas atskirai. Paprastai, kai prašoma tokių didelių paskirstymų, platintojas prašo naujų OS iš OS. Todėl yra didelė tikimybė, kad dideli pasirinkimai bus rodomi su tuo pačiu nuokrypiu nuo puslapio krašto.

Čia yra bandymo kodas:

 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; } 

Bandymo rezultatai:

EDIT: „Core 2 Real Architecture“ rezultatai:

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 

pastabos:

  • 6,206 sekundės su vienu ciklu ir 2.116 sekundėmis su dviem ciklais. Tai tiksliai atkuria OP rezultatus.

  • Per pirmuosius du testus matricos skirstomos atskirai. Jūs pastebėsite, kad visi jie turi tą patį lyginimą, palyginti su puslapiu.

  • Antruose dviejuose bandymuose matricos yra supakuotos kartu, kad nutrauktų šį derinimą. Čia pastebėsite, kad abu ciklai yra greitesni. Be to, antrasis (dvigubas) ciklas yra lėtesnis, kaip paprastai tikitės.

Kaip pastabos @Stephen Cannon komentaruose, yra labai tikėtina galimybė, kad šis derinimas sukelia klaidingą išlyginimą dėl apkrovos / saugojimo ar talpyklos. Aš maniau apie tai ir sužinojau, kad „Intel“ iš tikrųjų turi aparatinės įrangos skaitiklį daliniams adresams išlyginti :

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


border=0

5 Regionai - Paaiškinimai

1 regionas:

Tai lengva. Duomenų rinkinys yra toks mažas, kad vyrauja pridėtinės išlaidos, pvz., Ciklas ir šakos.

2 regionas:

Čia, kai duomenų dydžiai didėja, santykinių pridėtinių išlaidų skaičius mažėja, o našumas yra „prisotintas“. Čia du ciklai yra lėtesni, nes juose yra dvigubai daugiau sriegių ir šakų.

Nesu tikras, kas čia vyksta ... Derinimas vis dar gali turėti poveikį, nes Agner Fog pamini bankų talpyklos konfliktus . (Ši nuoroda taikoma „Sandy Bridge“, tačiau idėja turėtų būti taikoma „Core 2“.)

3 regionas:

Šiuo metu duomenys nebeatitinka L1 talpyklos. Taigi našumą riboja juostos plotis L1 ↔ L2.

4 regionas:

Vieno ciklo našumo sumažėjimas yra tai, ką stebime. Ir, kaip jau minėta, tai yra dėl derinimo, kuris (greičiausiai) sukelia klaidingą slapyvardžių blokavimą procesoriaus apkrovos / saugojimo blokuose.

Tačiau norint, kad atsirastų klaidingas išlyginimas, tarp duomenų rinkinių turi būti pakankamai didelis žingsnis. Štai kodėl jūs to nematote regione 3.

5 regionas:

Šiuo metu talpykloje nėra nieko. Taigi jūs esate prijungtas prie atminties pralaidumo.


border=0

2019

1585 m
18 дек. Atsakymas pateikiamas Mysticial 18 Dec. 2011-12-18 00:17 '11 prie 0:17 2011-12-18 00:17

Gerai, teisingas atsakymas tikrai turėtų kažką daryti su procesoriaus talpykla. Bet naudojant talpyklos argumentą gali būti gana sudėtinga, ypač be duomenų.

Yra daug atsakymų, dėl kurių vyko daug diskusijų, bet leiskite jiems susidurti su šia problema: talpyklų problemos gali būti labai sudėtingos, o ne viena dimensija. Jie labai priklauso nuo duomenų dydžio, todėl mano klausimas buvo nesąžiningas: jis pasirodė esąs labai įdomus talpyklos grafike.

@ Mistinis atsakymas įtikino daugelį žmonių (įskaitant mane), tikriausiai todėl, kad jis buvo vienintelis, kuris, atrodo, rėmėsi faktais, bet tai buvo tik vienas „tiesos duomenų taškas“.

Štai kodėl aš sujungiau savo testą (naudojant nuolatinį ar atskirą paskirstymą) ir atsakymą @James Answer.

Toliau pateiktuose grafikuose matyti, kad, atsižvelgiant į konkretų scenarijų ir naudojamus parametrus, dauguma atsakymų ir ypač daugelis pastabų ir atsakymų gali būti laikomi visiškai neteisingais arba teisingais.

Atkreipkite dėmesį, kad mano pradinis klausimas buvo n = 100.000 . Šis punktas (atsitiktinai) turi ypatingą elgesį:

  • Tai didžiausias neatitikimas tarp vienos ir dviejų ciklo versijų (beveik tris kartus)

  • Tai vienintelis taškas, kur vieno kilpos (ty nuolatinis paskirstymas) viršija dviejų kontūrų versiją. (Tai padarė mistinį atsakymą).

Rezultatas naudojant inicijuotus duomenis:

border=0

2019

203
18 дек. Johannes Gerer atsakymas gruodžio 18 d 2011-12-18 04:29 '11 at 4:29 2011-12-18 04:29

Antrasis ciklas apima daug mažesnę talpyklos veiklą, todėl procesorius yra lengviau palaikyti atminties reikalavimus.

69
17 дек. Atsakyti atsižvelgiant į šuniuką 17 d 2011-12-17 23:47 '11, 23:47, 2011-12-17 23:47

Įsivaizduokite, kad dirbate su mašina, kurioje n buvo teisinga vertė, kad galėtumėte vienu metu saugoti dvi savo masyvus atmintyje, tačiau bendras turimų atminties kiekis per disko talpyklą vis dar buvo pakankamas visiems keturiems laikyti.

Darant prielaidą, kad yra paprastas LIFO talpyklos laikymo politika, šis kodas:

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

pirmiausia sukels a ir b įkrovimą į RAM ir tada visiškai veiks RAM. Kai prasideda antrasis ciklas, c ir d įkeliami iš disko į atmintį ir darbą.

kitą ciklą

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

kiekvieną kartą aplink kilpą išeis du masyvai ir du puslapiai. Tai, žinoma, bus daug lėčiau.

Tikriausiai nematote diskų talpyklos jūsų bandymuose, tačiau tikriausiai matote šalutinį šalutinį spartinimo būdą.


Atrodo, kad čia yra šiek tiek painiavos / nesusipratimų, todėl bandysiu šiek tiek paaiškinti.

Pasakykite n = 2 ir mes dirbame su baitais. Taigi, mano scenarijuje, mes turime tik 4 baitus RAM, o likusi atmintis yra daug lėčiau (tarkim, 100 kartų daugiau prieigos).

Darant prielaidą, kad talpykla yra gana kvaila, jei baitas nėra talpykloje, įdėkite jį ir gaukite kitą baitą, kol mes jį laikysime, gausite tokį scenarijų:

  • Su

     for(int j=0;j<n;j++){ a[j] += b[j]; } for(int j=0;j<n;j++){ c[j] += d[j]; } 
  • mes talpome a[0] ir a[1] tada b[0] ir b[1] ir nustatome a[0] = a[0] + b[0] talpyklą - dabar yra keturi baitai talpykloje, a[0], a[1] ir b[0], b[1] . Kaina = 100 + 100.

  • nustatykite a[1] = a[1] + b[1] talpykloje. Kaina = 1 + 1.
  • Pakartokite c ir d .
  • Bendra kaina = (100 + 100 + 1 + 1) * 2 = 404

  • Su

     for(int j=0;j<n;j++){ a[j] += b[j]; c[j] += d[j]; } 
  • mes talpome a[0] ir a[1] tada b[0] ir b[1] ir nustatome a[0] = a[0] + b[0] talpyklą - dabar yra keturi baitai talpykloje, a[0], a[1] ir b[0], b[1] . Kaina = 100 + 100.

  • pašalinkite a[0], a[1], b[0], b[1] iš talpyklos ir talpyklą c[0] ir c[1] tada d[0] ir d[1] ir nustatykite c[0] = c[0] + d[0] talpykloje. Kaina = 100 + 100.
  • Įtariu, kad pradedate matyti, kur aš einu.
  • Bendra kaina = (100 + 100 + 100 + 100) * 2 = 800

Tai klasikinis šiukšlių talpyklos scenarijus.

41
18 дек. Atsakymą pateikė OldCurmudgeon 18 d. 2011-12-18 04:36 '11 at 4:36 2011-12-18 04:36

Tai nėra dėl kito kodo, bet dėl ​​spartinimo: RAM yra lėčiau nei procesorių registrai, o talpykla yra CPU viduje, kad būtų išvengta atminties rašymo kiekvieną kartą keičiant kintamąjį. Tačiau talpykla yra nedidelė, nes RAM, taigi, rodo tik dalį jos.

Pirmasis kodas keičia nuotolinės atminties adresus, juos keisdamas kiekviename cikle, todėl nuolat reikia atmesti talpyklą.

Antrasis kodas nepakeičiamas: jis paprasčiausiai perkeliamas į gretimus adresus du kartus. Dėl to visos užduotys atliekamos talpykloje, ją atšaukus tik pradėjus antrąjį ciklą.

29
17 дек. Emilio Garavaglia atsakymas, pateiktas gruodžio 17 d. 2011-12-17 23:49 „11, 11:49 PM 2011-12-17 23:49

Aš negaliu pakartoti čia aptartų rezultatų.

Nežinau, ar blogas testo kodas yra kaltas, ar tai, bet šie du metodai yra ne daugiau kaip 10% vienas nuo kito mano mašinoje, naudojant šį kodą, o vienas ciklas paprastai yra šiek tiek greitesnis nei du - kaip tikitės.

Array dydžiai svyravo nuo 2 ^ 16 iki 2 ^ 24, naudojant aštuonis ciklus. Buvau atsargus, kad inicijuotų originalias masyvus taip, kad priskyrimas += neprašė FPU pridėti atminties šiukšlių, interpretuotų kaip dvigubas.

Aš grojau su įvairiomis schemomis, pvz., Užduoties b[j] , d[j] InitToZero[j] į InitToZero[j] ciklų viduje, taip pat naudojant += b[j] = 1 ir += d[j] = 1 , ir Gavau gana nuoseklius rezultatus.

Kaip galima tikėtis, b ir d inicijavimas kontūro viduje naudojant InitToZero[j] suteikė kombinuotam metodui pranašumą, nes jie buvo atlikti glaudžiai prieš paskiriant a ir c , bet vis dar per 10%. Eiti išsiaiškinkite.

Aparatūra - „ Dell XPS 8500“ su 3 Core i7 @ 3.4 GHz procesoriumi ir 8 GB atmintimi. 2 ^ 16 - 2 ^ 24, naudojant aštuonis ciklus, bendrasis laikas buvo atitinkamai 44 987 ir 40 965. „Visual C ++ 2010“ yra visiškai optimizuotas.

PS: aš pakeitiau skaičiavimo ciklus į nulį, o bendras metodas buvo šiek tiek greitesnis. Nulaužykite galvą Atkreipkite dėmesį į naują masyvo dydį ir ciklų skaičių.

 // 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; } 

Nesu tikras, kodėl buvo nuspręsta, kad MFLOPS buvo svarbus rodiklis. Nors idėja buvo sutelkti dėmesį į prieigą prie atminties, todėl bandžiau sumažinti plaukiojimo laiką. Nuėjau į += , bet nesu įsitikinęs, kodėl.

Tiesioginis priskyrimas be skaičiavimų būtų tikslesnis atminties prieigos laiko testas ir sudarytų testą, kuris būtų vienodas, neatsižvelgiant į ciklų skaičių. Gal aš praleidau kažką pokalbyje, bet tai verta pamąstyti du kartus. Jei pliusas nėra įtrauktas į užduotį, kaupiamasis laikas yra beveik tas pats ir yra 31 sekundė.

18
30 дек. Atsakymą pateikė vartotojo1899861 30 gruodis. 2012-12-30 04:34 '12 ne 4:34 2012-12-30 04:34

Taip yra todėl, kad procesorius neturi daug talpyklų (kai jis turi laukti, kol matricos duomenys bus gauti iš RAM lustų). Būtų įdomu visą laiką sureguliuoti matricų dydį, kad viršytumėte 1 (L1) talpyklos lygio ir tada jūsų procesoriaus 2 talpyklos lygį (L2) ir apskaičiuotumėte laiką, praleistą kodo vykdymui pagal masyvų dydį. Grafikas neturėtų būti tiesioginis, kaip tikėtasi.

15
17 дек. Jameso atsakymas 17 d 2011-12-17 23:52 „11, 11:52 PM 2011-12-17 23:52

Pirmoji kilpa pakaitomis įveda kiekvieną kintamąjį. Antrasis ir trečiasis tik atlieka mažų elementų dydžio šuolius.

Stenkitės parašyti dvi lygiagrečias 20 kryžių eilutes su rašikliu ir lapu, atskirtomis 20 centimetrų. Pabandykite vieną ir kitą eilutę vieną kartą baigti ir bandyti dar kartą paspaudę kryžiuką kiekvienoje eilutėje.

13
17 авг. atsakymas pateikiamas Guillaume Kiz 17 rug. 2012-08-17 18:23 '12, 18:23, 2012-08-17 18:23

Originalus klausimas

Kodėl vienas ciklas yra daug lėčiau nei du?


Išvada:

1 atvejis yra klasikinė interpoliacijos problema, kuri yra neveiksminga. Taip pat manau, kad tai buvo viena iš pagrindinių priežasčių, kodėl daugelis mašinų architektūrų ir kūrėjų baigė kurti ir kurti daugiasistemines sistemas, galinčias paleisti daugiaspaudes programas, taip pat lygiagrečią programavimą.

Atsižvelgiant į tai naudojant šį metodą, nedarant įtakos aparatūros, OS ir kompiliatoriaus (-ų) darbui, kad būtų paryškintas krūva, kuri apima darbą su RAM, talpyklomis, ieškos failais ir tt; Šių algoritmų pagrindu matematika rodo, kuris iš šių dviejų variantų yra geriausias sprendimas. Mes galime naudoti analogiją, kur Boss arba Summation , kuris bus „ For Loop , kuris turėtų judėti tarp A B darbuotojų, lengvai matome, kad 2 atvejis yra mažiausiai 1/2 , kaip greitai, jei ne daugiau, 1 atvejis dėl skirtingo atstumo, reikalingo kelionei, ir laiko, praleisto tarp darbuotojų. Ši matematika yra beveik virtuali ir puikiai atitinka tiek „Bench Mark Times“, tiek ir montavimo instrukcijų skirtumus.

Dabar pradėsiu paaiškinti, kaip tai veikia žemiau.


Problemos įvertinimas

OP kodas:

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

Taip pat

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

Apsvarstymas