Zašto je u odvojenim ciklusima dodatak stigmi mnogo brži nego u kombiniranom ciklusu?

Pretpostavimo da a1 , b1 , c1 i d1 upućuju na hrpu memorije, a moj numerički kod ima sljedeću glavnu petlju.

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

Ova petlja se izvršava 10.000 puta kroz drugu vanjsku petlju. Da bih ga ubrzao, promijenio sam kôd na:

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

Sastavljen na MS Visual C ++ 10.0 s potpunom optimizacijom i SSE2 omogućen za 32-bitni na Intel Core 2 Duo (x64), prvi primjer traje 5,5 sekundi, a primjer s dvostrukom petljom traje samo 1,9 sekundi. Moje pitanje glasi: (pogledajte moje pitanje u nastavku)

PS: Nisam siguran hoće li to pomoći:

Rastavljanje za prvi ciklus u osnovi izgleda ovako (ovaj blok se ponavlja oko pet puta u punom programu):

 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] 

Svaka petlja u primjeru dvostruke petlje kreira ovaj kôd (sljedeći blok se ponavlja oko tri puta):

 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 

Pitanje se pokazalo irelevantnim, jer ponašanje ovisi o veličini polja (n) i CPU-a. Dakle, ako postoji daljnje zanimanje, preformuliram pitanje:

Možete li dati detaljan uvid u pojedinosti koje vode do različitih ponašanja predmemorije, kao što je prikazano u pet područja na sljedećem grafikonu?

Također bi bilo zanimljivo istaknuti razlike između CPU-a i arhitekture predmemorije, pružajući sličan raspored za ove CPU-ove.

PPS: ovdje je cijeli kod. Koristi TBB Tick_Count za sinkronizaciju s višom razlučivošću, koja se može onemogućiti bez navođenja 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; } } 

(Prikazuje FLOP / s za različite vrijednosti od n .)

2019

2073
17 дек. Johannes Gerer postavljen je 17. prosinca. 2011-12-17 23:40 '11 u 23:40 2011-12-17 23:40
@ 10 odgovora

Nakon daljnje analize ovoga, vjerujem da je to (barem djelomično) posljedica poravnanja četiri pokazivača. To će dovesti do konflikta cache / path.

Ako ispravno razumijem kako dodjeljujete nizove, oni će najvjerojatnije biti poravnati s nizom stranica .

To znači da će svi vaši pozivi u svakom ciklusu pasti u istu datoteku predmemorije. Međutim, Intelovi procesori već neko vrijeme imaju 8-staznu L1 cache asocijativnost. Ali zapravo izvedba nije posve ujednačena. Pristup 4-kanalnim kanalima i dalje je sporiji nego dvosmjerni.

EDIT: Zapravo, izgleda da odabirete sve nizove zasebno. Obično, kada se traži tako velika alokacija, distributer traži nove stranice iz OS-a. Stoga postoji velika vjerojatnost da će veliki odabiri biti prikazani s istim odmakom od granice stranice.

Evo test koda:

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

Rezultati testa:

EDIT: Rezultati na Core 2 stvarnoj arhitekturi:

2 x Intel Xeon X5482 Harpertown na 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 

komentari:

  • 6.206 sekundi s jednim ciklusom i 2.116 sekundi s dva ciklusa. To točno reproducira rezultate OP-a.

  • U prva dva testa, polja se dodjeljuju zasebno. Primijetit ćete da svi imaju isto poravnanje u odnosu na stranicu.

  • U druga dva testa, nizovi su pakirani zajedno kako bi se prekinula ta poravnanja. Ovdje ćete primijetiti da su oba ciklusa brža. Osim toga, drugi (dvostruki) ciklus je sada sporiji, što obično očekujete.

U bilješkama @Stephen Cannon postoji vrlo vjerojatna mogućnost da ovo poravnanje uzrokuje lažno izglađivanje u smislu učitavanja / pohrane ili predmemorije. Razmišljao sam o tome i otkrio da Intel zapravo ima hardverski brojač za izglađivanje djelomičnih adresa :

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


border=0

5 Regije - Objašnjenja

Regija 1:

Lako je. Skup podataka je toliko mali da prevladavaju opći troškovi kao što su ciklus i grananje.

Regija 2:

Ovdje se, kada se veličina podataka poveća, broj relativnih troškova smanjuje, a učinkovitost je "zasićena". Ovdje su dva ciklusa sporiji, jer ima dvostruko više niti i grana.

Nisam siguran što se ovdje događa ... Usklađivanje još uvijek može imati učinak, budući da Agner Fog spominje konflikte u bankovnom kešu . (Ova veza odnosi se na Sandy Bridge, ali ideja bi se trebala primjenjivati ​​na Core 2.)

Regija 3:

U ovom trenutku, podaci se više ne uklapaju u L1 predmemoriju. Prema tome, izvedba je ograničena širinom pojasa L1 2 L2.

Regija 4:

Smanjenje učinka u jednom ciklusu je ono što promatramo. I, kao što je već spomenuto, to je zbog poravnanja, koje (najvjerojatnije) uzrokuje blokiranje lažnih pseudonima u blokovima procesora opterećenja / pohrane.

Međutim, da bi došlo do lažnog izglađivanja, između skupova podataka mora postojati dovoljno velik korak. Zato ga ne vidite u regiji 3.

Regija 5:

U ovom trenutku, ništa se ne uklapa u predmemoriju. Tako ste povezani memorijskom propusnošću.


border=0

2019

1585
18 дек. Odgovor je dan Mysticial 18. prosinca. 2011-12-18 00:17 '11 u 0:17 2011-12-18 00:17

U redu, točan odgovor svakako bi trebao učiniti nešto s predmemorijom procesora. No, korištenje argumenta predmemorije može biti vrlo komplicirano, pogotovo bez podataka.

Postoji mnogo odgovora koji su doveli do mnogo rasprava, ali neka se suoče s tim: problemi s predmemorijom mogu biti vrlo složeni i ne jednodimenzionalni. Oni uvelike ovise o veličini podataka, pa je moje pitanje bilo nepošteno: ispostavilo se da je vrlo zanimljivo u grafikonu predmemorije.

@ Mističan odgovor uvjerio je mnoge ljude (uključujući i mene), vjerojatno zato što je on jedini koji se, čini se, oslanjao na činjenice, ali to je bila samo jedna “točka podataka” istine.

Zato sam spojio njegov test (koristeći kontinuiranu ili odvojenu distribuciju) i odgovor @James Answer.

Donji grafikoni pokazuju da se većina odgovora, a posebno većina komentara na pitanje i odgovore, može smatrati potpuno pogrešnim ili istinitim, ovisno o specifičnom scenariju i korištenim parametrima.

Napominjem da je moje prvo pitanje bilo n = 100.000 . Ova točka (slučajno) pokazuje posebno ponašanje:

  • Ima najveću razliku između jedne i dvije verzije ciklusa (gotovo tri puta)

  • To je jedina točka u kojoj jednostruka petlja (naime, kontinuirana distribucija) prelazi dvoprugu inačicu. (To je omogućilo mističan odgovor).

Rezultat pomoću inicijaliziranih podataka:

border=0

2019

203
18 дек. Odgovor koji je dao Johannes Gerer Dec 18 2011-12-18 04:29 '11 u 4:29 2011-12-18 04:29

Drugi ciklus uključuje mnogo manje aktivnosti predmemorije, tako da je procesor lakše održavati memorijske zahtjeve.

69
17 дек. Odgovor daje Puppy Dec 17 2011-12-17 23:47 '11 u 23:47 2011-12-17 23:47

Zamislite da radite na stroju gdje je n ispravna vrijednost, tako da možete istovremeno pohraniti dva polja u memoriju, ali je ukupna količina raspoložive memorije preko spremišta diska još uvijek bila dovoljna za pohranu svih četiriju.

Pod pretpostavkom jednostavne LIFO pravila predmemoriranja, ovaj kôd:

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

prvo će uzrokovati učitavanje a i b u RAM, a zatim potpuno raditi u RAM-u. Kada drugi ciklus započne, c i d zatim učitavaju s diska u RAM i rade.

drugi ciklus

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

će izlaziti dva niza i stranicu u dvije druge svaki put oko petlje . To će očito biti puno sporiji.

Vjerojatno ne vidite predmemoriranje diska u testovima, ali vjerojatno vidite nuspojave nekog drugog oblika predmemoriranja.


Čini se da je ovdje malo zbrke / nesporazuma, pa ću pokušati malo pojasniti na primjer.

Ispišite n = 2 i radimo s bajtovima. Dakle, u mom scenariju imamo samo 4 bajta RAM-a, a ostatak memorije je puno sporiji (recimo, 100 puta više pristupa).

Ako pretpostavimo prilično glupu caching politiku, ako bajt nije u cacheu, stavite ga tamo i dobijete sljedeći bajt dok smo na njemu, dobivate skriptu poput ove:

  • C

     for(int j=0;j<n;j++){ a[j] += b[j]; } for(int j=0;j<n;j++){ c[j] += d[j]; } 
  • mi cache a[0] i a[1] zatim b[0] i b[1] i postavite a[0] = a[0] + b[0] u cache - sada postoje četiri bajta u cacheu, a[0], a[1] i b[0], b[1] . Trošak = 100 + 100.

  • postavite a[1] = a[1] + b[1] u cache. Trošak = 1 + 1.
  • Ponovite za c i d .
  • Ukupni trošak = (100 + 100 + 1 + 1) * 2 = 404

  • C

     for(int j=0;j<n;j++){ a[j] += b[j]; c[j] += d[j]; } 
  • mi cache a[0] i a[1] zatim b[0] i b[1] i postavite a[0] = a[0] + b[0] u cache - sada postoje četiri bajta u cacheu, a[0], a[1] i b[0], b[1] . Trošak = 100 + 100.

  • uklonite a[0], a[1], b[0], b[1] iz cachea i cache c[0] i c[1] zatim d[0] i d[1] i postavite c[0] = c[0] + d[0] u cacheu. Trošak = 100 + 100.
  • Pretpostavljam da počinjete vidjeti gdje idem.
  • Ukupni trošak = (100 + 100 + 100 + 100) * 2 = 800

Ovo je klasični scenarij za smeće.

41
18 дек. Odgovor je dao OldCurmudgeon 18. prosinca. 2011-12-18 04:36 '11 u 4:36 2011-12-18 04:36

To nije zbog različitog koda, već zbog keširanja: RAM je sporiji od registra procesora, a predmemorija se nalazi u CPU-u kako bi se izbjeglo pisanje RAM-a svaki put kad se promijeni varijabla. No, predmemorija je mala, jer RAM, dakle, prikazuje samo dio.

Prvi kôd mijenja adrese udaljene memorije, izmjenjujući ih u svakom ciklusu, što zahtijeva stalno odbacivanje predmemorije.

Drugi kod se ne mijenja: on se jednostavno premješta na susjedne adrese dva puta. To dovodi do dovršavanja svih zadataka u predmemoriji, poništavanjem samo nakon pokretanja drugog ciklusa.

29
17 дек. Odgovor koji je dao Emilio Garavaglia 17. prosinca 2011-12-17 23:49 '11 u 23:49 2011-12-17 23:49

Ne mogu ponoviti ovdje raspravljene rezultate.

Ne znam je li kriv loš testni kôd, ili što, ali ove dvije metode su unutar 10% jedna od druge na mom računalu pomoću sljedećeg koda, a jedan ciklus je obično nešto brži od dva - kao što biste očekivali.

Veličine polja su se kretale od 2 ^ 16 do 2 ^ 24 pomoću osam ciklusa. Bila sam oprezna da inicijaliziram izvorne nizove tako da zadatak += nije tražio od FPU-a da doda smeće memorije, interpretirano kao dvostruko.

Igrao sam s raznim shemama, kao što je postavljanje zadatka b[j] , d[j] za InitToZero[j] unutar ciklusa, kao i pomoću += b[j] = 1 i += d[j] = 1 , i Dobio sam prilično dosljedne rezultate.

Kao što se moglo i očekivati, inicijalizacija b i d unutar petlje pomoću InitToZero[j] dala je prednost kombiniranom pristupu, budući da su izvršeni blisko prije dodjeljivanja a i c , ali još uvijek unutar 10%. Idi shvatiti.

Hardver - Dell XPS 8500 s 3 Core i7 @ 3.4 GHz procesorom i 8 GB memorije. Za 2 ^ 16 do 2 ^ 24, koristeći osam ciklusa, ukupno vrijeme je bilo 44,987 i 40,965. Visual C ++ 2010 je potpuno optimiziran.

PS: Promijenio sam cikluse odbrojavanja na nulu, a kombinirana metoda bila je malo brža. Grebanje glave Imajte na umu novu veličinu polja i broj ciklusa.

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

Nisam siguran zašto je odlučeno da je MFLOPS relevantan pokazatelj. Iako je ideja bila usredotočiti se na pristup memoriji, pokušao sam smanjiti vrijeme plutanja. Otišao sam na += , ali nisam siguran zašto.

Izravan zadatak bez izračuna bio bi točniji test vremena pristupa memoriji i stvorio bi test koji bi bio ujednačen bez obzira na broj ciklusa. Možda sam nešto propustio u razgovoru, ali ovo je vrijedno razmišljanja dvaput. Ako plus nije uključen u zadatak, kumulativno vrijeme je gotovo isto i iznosi 31 sekundi.

18
30 дек. Odgovor daje korisnik1899861 30. prosinca. 2012-12-30 04:34 '12 u 4:34 2012-12-30 04:34

To je zato što procesor nema toliko promašaja predmemorije (gdje mora čekati niz podataka koji dolaze iz RAM čipova). Bilo bi zanimljivo prilagoditi veličinu nizova cijelo vrijeme tako da premašite veličinu razine cachea 1 (L1), a zatim razinu cachea 2 (L2) vašeg procesora i izračunate vrijeme provedeno na izvršenju koda u odnosu na veličinu polja. Grafikon ne bi trebao biti izravan, kao što bi se očekivalo.

15
17 дек. odgovor koji je dao James 17. prosinca 2011-12-17 23:52 '11 u 23:52 2011-12-17 23:52

Prva petlja zamjenjuje unos u svakoj varijabli. Drugi i treći čine samo skokove malih veličina.

Pokušajte napisati dvije paralelne linije od 20 križeva s olovkom i plahtom, odvojene 20 centimetara. Pokušajte završiti jednu, a zatim još jednu liniju i pokušajte ponovno pritiskanjem križa u svakom redu naizmjence.

13
17 авг. odgovor je dat Guillaume Kiz 17 aug. 2012-08-17 18:23 '12 u 18:23 2012-08-17 18:23

Izvorno pitanje

Zašto je jedan ciklus mnogo sporiji od dva?


zaključak:

Slučaj 1 je klasični interpolacijski problem koji je neučinkovit. Također mislim da je to bio jedan od glavnih razloga zašto su mnoge arhitekture strojeva i programeri dovršili izradu i projektiranje višestrukih sustava s mogućnošću izvođenja višestrukih aplikacija, kao i paralelnog programiranja.

Imajući u vidu ovaj pristup, bez utjecaja na način na koji hardver, OS i kompajler (i) rade zajedno kako bi istaknuli hrpu, što uključuje rad s RAM-om, predmemorijom, straničnom datotekom, itd.; Matematika na kojoj se temelje ti algoritmi pokazuje nam koja je od ove dvije opcije najbolje rješenje. Možemo koristiti analogiju, gdje Boss ili Summation , koja će biti For Loop , koja bi se trebala kretati između A B radnika, lako možemo vidjeti da je slučaj 2 najmanje 1/2 , kako brzo, ako ne i malo više, slučaj 1 zbog razlike u udaljenosti koja je potrebna za putovanje i vremena provedenog između radnika. Ova je matematika gotovo virtualna i savršeno se podudara s vremenima Bench Mark Timesa i razlikom u uputama za montažu.

Sada ću početi objašnjavati kako sve to radi ispod.


Procjena problema

OP kod:

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

Kao dobro

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

razmatranje

Uzimajući u obzir izvorno OP pitanje o dvije varijante za petlje i njegovo ispravljeno pitanje o ponašanju spremnika, kao i mnoge druge odlične odgovore i korisne komentare; Я хотел бы попытаться сделать что-то другое здесь, используя другой подход к этой ситуации и проблеме.


Подход