Mai exact, dacă am avea o serie de "dacă" ... "altfel dacă" declarații, și cumva am știut dinainte probabilitatea relativă pe care fiecare declarație se va evalua la "adevărat", cât de mult de o diferență în timp de executie nu-l face să le sorta în ordinea de probabilitatea? De exemplu, ar trebui să o prefer pe aceasta:
if (highly_likely)
//do something
else if (somewhat_likely)
//do something
else if (unlikely)
//do something
pentru acest lucru?:
if (unlikely)
//do something
else if (somewhat_likely)
//do something
else if (highly_likely)
//do something
Pare evident că sortate versiune ar fi mai rapid, cu toate acestea, pentru lizibilitate sau existența unor efecte secundare, ne-am putea dori pentru a le comanda non-optim. L's, de asemenea, greu de spus cât de bine unitatea de PROCESOR central va face cu ramura de predicție până când de fapt rula codul.
Deci, în curs de experimentare cu asta, am ajuns sa-mi răspund singur la întrebare pentru un caz specific, cu toate acestea am'd place să aud și alte opinii/perspective la fel de bine.
Important: această întrebare presupune că "dacă" declarații pot fi reordonate în mod arbitrar, fără a avea alte efecte asupra comportamentului programului. În răspunsul meu, cele trei condiționată teste se exclud reciproc și nu produc efecte secundare. Cu siguranță, dacă declarațiile trebuie să fie evaluate într-o anumită ordine pentru a obține un comportament dorit, atunci problema de eficiență este discutabilă.
Ca regulă generală, cele mai multe, dacă nu toate Procesoarele Intel presupune înainte ramuri nu sunt luate prima dată când le văd. A se vedea Godbolt's lucreze.
După aceea, sucursala merge într-o ramură de predicție cache, și comportamentul trecut este folosit pentru a informa viitorul ramură de predicție.
Deci, într-o buclă strânsă, efectul de misordering va fi relativ mic. Branch predictor este de gând să învețe care set de ramuri este cel mai probabil, și dacă aveți non-trivial volumul de muncă în buclă mici diferențe câștigat't adăugați prea mult.
În general, codul, cele mai multe compilatoare în mod implicit (fără alt motiv) va comanda produs masina de cod aproximativ cum l-ai comandat în cod. Astfel, dacă declarațiile sunt departe de filiale, atunci când nu reușesc.
Deci, ar trebui să comanda ramuri, în ordinea descrescătoare a riscului pentru a obține cele mai bune ramură de predicție de la un "prima întâlnire".
Un microbenchmark că bucle bine de mai multe ori un set de condiții și nu banal lucru este de gând să dominat de mici efecte de instruire conta și ca, și în raport de ramura de predicție probleme. Deci, în acest caz trebuie de profil, ca reguli de degetul mare a câștigat't fi de încredere.
Pe deasupra, vectorizare și multe alte optimizări se aplică mici bucle strânse.
Deci, în general, codul, a pus cel mai probabil codul în "dacă" bloc, și care va avea ca rezultat mai putine onu-în cache branch prediction dor. În strânsă bucle, urmați regula generală pentru a începe, și dacă aveți nevoie să știți mai multe nu prea ai de ales, dar la profil.
Bineînțeles, acest lucru duce totul pe fereastră, dacă unele teste sunt mult mai ieftine decât altele.
Am făcut următorul test pentru executarea a două tipuri diferite de "dacă" ... "altfel dacă" blocuri, unul sortate în ordine de probabilitate, alte sortate în ordine inversă:
#include <chrono>
#include <iostream>
#include <random>
#include <algorithm>
#include <iterator>
#include <functional>
using namespace std;
int main()
{
long long sortedTime = 0;
long long reverseTime = 0;
for (int n = 0; n != 500; ++n)
{
//Generate a vector of 5000 random integers from 1 to 100
random_device rnd_device;
mt19937 rnd_engine(rnd_device());
uniform_int_distribution<int> rnd_dist(1, 100);
auto gen = std::bind(rnd_dist, rnd_engine);
vector<int> rand_vec(5000);
generate(begin(rand_vec), end(rand_vec), gen);
volatile int nLow, nMid, nHigh;
chrono::time_point<chrono::high_resolution_clock> start, end;
//Sort the conditional statements in order of increasing likelyhood
nLow = nMid = nHigh = 0;
start = chrono::high_resolution_clock::now();
for (int& i : rand_vec) {
if (i >= 95) ++nHigh; //Least likely branch
else if (i < 20) ++nLow;
else if (i >= 20 && i < 95) ++nMid; //Most likely branch
}
end = chrono::high_resolution_clock::now();
reverseTime += chrono::duration_cast<chrono::nanoseconds>(end-start).count();
//Sort the conditional statements in order of decreasing likelyhood
nLow = nMid = nHigh = 0;
start = chrono::high_resolution_clock::now();
for (int& i : rand_vec) {
if (i >= 20 && i < 95) ++nMid; //Most likely branch
else if (i < 20) ++nLow;
else if (i >= 95) ++nHigh; //Least likely branch
}
end = chrono::high_resolution_clock::now();
sortedTime += chrono::duration_cast<chrono::nanoseconds>(end-start).count();
}
cout << "Percentage difference: " << 100 * (double(reverseTime) - double(sortedTime)) / double(sortedTime) << endl << endl;
}
Folosind MSVC2017 cu /O2, rezultatele arată că sortate versiune este în mod constant aproximativ 28% mai rapid decât nesortate versiune. Pe luk32's a comenta, de asemenea, am pornit de ordinul a două teste, care face o diferență notabilă (22% vs 28%). Codul a fost rulat sub Windows 7 pe un procesor Intel Xeon E5-2697 v2. Acest lucru este, desigur, o problemă foarte specifice și nu ar trebui să fie interpretată ca un răspuns convingător.
Nu ar trebui să nu, dacă nu sunteți sigur că sistemul țintă este afectat. În mod implicit du-te de lizibilitate.
Mă îndoiesc de rezultatele tale. Am'am modificat exemplu un pic, astfel încât inversarea de execuție este mai ușor. Ideone mai degrabă arată în mod constant că inversa-comanda este mai rapid, deși nu cu mult. Pe anumite ruleaza chiar acest lucru ocazional oglindită. Am'd spune că rezultatele sunt neconcludente. coliru raportează nici o diferență reală la fel de bine. Eu pot verifica Exynos5422 CPU pe odroid xu4 mai târziu.
Chestia e că Procesoare moderne au ramură de predictori. Nu este mult-mult logica dedicat pre-preluarea atât de date și instrucțiuni, și moderne Procesoare x86 sunt destul de inteligent, atunci când vine vorba de acest lucru. Unele mai subtire arhitecturi, cum ar fi Arme sau Gpu-uri ar putea fi vulnerabile la aceasta. Dar este într-adevăr depinde foarte mult de atât compiler și sistemul țintă.
Aș spune că filiala comanda de optimizare este destul de fragil și efemer. O fac doar ca unele chiar de reglaj fin pas.
Cod:
#include <chrono>
#include <iostream>
#include <random>
#include <algorithm>
#include <iterator>
#include <functional>
using namespace std;
int main()
{
//Generate a vector of random integers from 1 to 100
random_device rnd_device;
mt19937 rnd_engine(rnd_device());
uniform_int_distribution<int> rnd_dist(1, 100);
auto gen = std::bind(rnd_dist, rnd_engine);
vector<int> rand_vec(5000);
generate(begin(rand_vec), end(rand_vec), gen);
volatile int nLow, nMid, nHigh;
//Count the number of values in each of three different ranges
//Run the test a few times
for (int n = 0; n != 10; ++n) {
//Run the test again, but now sort the conditional statements in reverse-order of likelyhood
{
nLow = nMid = nHigh = 0;
auto start = chrono::high_resolution_clock::now();
for (int& i : rand_vec) {
if (i >= 95) ++nHigh; //Least likely branch
else if (i < 20) ++nLow;
else if (i >= 20 && i < 95) ++nMid; //Most likely branch
}
auto end = chrono::high_resolution_clock::now();
cout << "Reverse-sorted: \t" << chrono::duration_cast<chrono::nanoseconds>(end-start).count() << "ns" << endl;
}
{
//Sort the conditional statements in order of likelyhood
nLow = nMid = nHigh = 0;
auto start = chrono::high_resolution_clock::now();
for (int& i : rand_vec) {
if (i >= 20 && i < 95) ++nMid; //Most likely branch
else if (i < 20) ++nLow;
else if (i >= 95) ++nHigh; //Least likely branch
}
auto end = chrono::high_resolution_clock::now();
cout << "Sorted:\t\t\t" << chrono::duration_cast<chrono::nanoseconds>(end-start).count() << "ns" << endl;
}
cout << endl;
}
}
Doar 5 cenți. Se pare că efectul de a comanda daca declarații ar trebui să depindă de:
Probabilitatea de fiecare daca declarație.
Numărul de iterații, astfel încât branch predictor-ar putea lovi cu piciorul în.
Probabil/puțin probabil compiler indicii, și anume codul layout.
Pentru a explora aceste elemente, evaluate următoarele funcții:
for (i = 0; i < data_sz * 1024; i++) {
if (data[i] < check_point) // highly likely
s += 3;
else if (data[i] > check_point) // samewhat likely
s += 2;
else if (data[i] == check_point) // very unlikely
s += 1;
}
for (i = 0; i < data_sz * 1024; i++) {
if (data[i] == check_point) // very unlikely
s += 1;
else if (data[i] > check_point) // samewhat likely
s += 2;
else if (data[i] < check_point) // highly likely
s += 3;
}
for (i = 0; i < data_sz * 1024; i++) {
if (likely(data[i] < check_point)) // highly likely
s += 3;
else if (data[i] > check_point) // samewhat likely
s += 2;
else if (unlikely(data[i] == check_point)) // very unlikely
s += 1;
}
for (i = 0; i < data_sz * 1024; i++) {
if (unlikely(data[i] == check_point)) // very unlikely
s += 1;
else if (data[i] > check_point) // samewhat likely
s += 2;
else if (likely(data[i] < check_point)) // highly likely
s += 3;
}
Matrice de date conține numere aleatoare intre 0 si 100:
const int RANGE_MAX = 100;
uint8_t data[DATA_MAX * 1024];
static void data_init(int data_sz)
{
int i;
srand(0);
for (i = 0; i < data_sz * 1024; i++)
data[i] = rand() % RANGE_MAX;
}
Următoarele rezultate sunt pentru Intel i5@3,2 GHz si G++ 6.3.0. Primul argument este check_point (adică probabilitatea ca în %% pentru foarte probabil din cauza faptului dacă declarația), cel de-al doilea argument este data_sz (de exemplu, numărul de iterații).
---------------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/4 4660 ns 4658 ns 150948
ordered_ifs/50/8 25636 ns 25635 ns 27852
ordered_ifs/75/4 4326 ns 4325 ns 162613
ordered_ifs/75/8 18242 ns 18242 ns 37931
ordered_ifs/100/4 1673 ns 1673 ns 417073
ordered_ifs/100/8 3381 ns 3381 ns 207612
reversed_ifs/50/4 5342 ns 5341 ns 126800
reversed_ifs/50/8 26050 ns 26050 ns 26894
reversed_ifs/75/4 3616 ns 3616 ns 193130
reversed_ifs/75/8 15697 ns 15696 ns 44618
reversed_ifs/100/4 3738 ns 3738 ns 188087
reversed_ifs/100/8 7476 ns 7476 ns 93752
ordered_ifs_with_hints/50/4 5551 ns 5551 ns 125160
ordered_ifs_with_hints/50/8 23191 ns 23190 ns 30028
ordered_ifs_with_hints/75/4 3165 ns 3165 ns 218492
ordered_ifs_with_hints/75/8 13785 ns 13785 ns 50574
ordered_ifs_with_hints/100/4 1575 ns 1575 ns 437687
ordered_ifs_with_hints/100/8 3130 ns 3130 ns 221205
reversed_ifs_with_hints/50/4 6573 ns 6572 ns 105629
reversed_ifs_with_hints/50/8 27351 ns 27351 ns 25568
reversed_ifs_with_hints/75/4 3537 ns 3537 ns 197470
reversed_ifs_with_hints/75/8 16130 ns 16130 ns 43279
reversed_ifs_with_hints/100/4 3737 ns 3737 ns 187583
reversed_ifs_with_hints/100/8 7446 ns 7446 ns 93782
Pentru 4K iterații și (aproape) 100% probabilitate extrem de placut declarație diferența este uriașă 223%:
ordered_ifs/100/4 1673 ns 1673 ns 417073 reversed_ifs/100/4 3738 ns 3738 ns 188087
Pentru 4K iterații și 50% probabilitate extrem de placut declarație diferența este de aproximativ 14%:
---------------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/4 4660 ns 4658 ns 150948
reversed_ifs/50/4 5342 ns 5341 ns 126800
Diferența între 4K și 8K iterații pentru (aproape) 100% probabilitate extrem de placut declarație este de aproximativ două ori (așa cum era de așteptat):
ordered_ifs/100/4 1673 ns 1673 ns 417073 ordered_ifs/100/8 3381 ns 3381 ns 207612
Dar diferența între 4K și 8K iterații pentru o probabilitate de 50% de foarte placut declarație este de 5,5 ori:
---------------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/4 4660 ns 4658 ns 150948
ordered_ifs/50/8 25636 ns 25635 ns 27852
De ce este așa? Din cauza branch predictor dor. Aici este ramura ratează pentru fiecare menționat cazul de mai sus:
ordered_ifs/100/4 0.01% of branch-misses
ordered_ifs/100/8 0.01% of branch-misses
ordered_ifs/50/4 3.18% of branch-misses
ordered_ifs/50/8 15.22% of branch-misses
Deci pe i5 branch predictor eșuează spectaculos pentru a nu-așa-probabil ramuri și seturi mari de date.
Pentru 4K iterații rezultatele sunt oarecum mai rău pentru o probabilitate de 50% și oarecum mai bine de aproape 100% probabilitate:
ordered_ifs/50/4 4660 ns 4658 ns 150948 ordered_ifs/100/4 1673 ns 1673 ns 417073 ordered_ifs_with_hints/50/4 5551 ns 5551 ns 125160 ordered_ifs_with_hints/100/4 1575 ns 1575 ns 437687
Dar pentru 8K iterații rezultatele sunt întotdeauna un pic mai bine:
---------------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/8 25636 ns 25635 ns 27852
ordered_ifs/100/8 3381 ns 3381 ns 207612
ordered_ifs_with_hints/50/8 23191 ns 23190 ns 30028
ordered_ifs_with_hints/100/8 3130 ns 3130 ns 221205
Astfel, indicii, de asemenea, de ajutor, dar doar un pic.
Concluzie generală este: întotdeauna de referință cod, deoarece rezultatele pot surprinde.
Sper că vă ajută.
Bazat pe unele dintre celelalte răspunsuri aici, se pare ca singura solutie reala este: depinde de. Depinde cel puțin următoarele (deși nu neapărat în această ordine de importanță):
Singura modalitate de a ști sigur este de a evalua cazul dumneavoastră specific, de preferință pe un sistem identice (sau similare) destinate sistemului pe care codul va rula în cele din urmă. Dacă acesta este destinat pentru a rula pe un set de diferite sisteme cu diferite hardware, sistem de operare, etc., atunci este o idee bună pentru a compara între mai multe variante pentru a vedea care este cel mai bun. Acesta poate fi chiar o idee bună să ai codul fi compilat cu o comanda pe un singur tip de sistem si o alta comanda pe un alt tip de sistem.
Mea personală regulă de degetul mare (pentru cele mai multe cazuri, în lipsa unui indice de referință) este de a comanda, bazat pe:
Modul în care am vedea, de obicei, acest lucru rezolvate pentru cod de înaltă performanță este păstrarea pentru că este mai ușor de citit, dar furnizarea de informatii de la compilator. Aici este un exemplu din Linux kernel:
if (likely(access_ok(VERIFY_READ, from, n))) {
kasan_check_write(to, n);
res = raw_copy_from_user(to, from, n);
}
if (unlikely(res))
memset(to + (n - res), 0, res);
De aici presupunerea este că accesul verifica va trece, și că nici o eroare este returnat în "res". Încercarea de a reordona dacă oricare dintre aceste clauze ar confunda cod, dar probabil () " și " puțin probabil()
macro-uri ajuta de fapt, lizibilitate, subliniind ceea ce este normal și ce este o excepție.
Linux punerea în aplicare a acestor macro-uri foloseste GCC caracteristici specifice. Se pare că zăngănit și Intel C compiler sprijin aceeași sintaxă, dar MSVC nu't au o astfel de caracteristică.
De asemenea, depinde de compilator și platforma pentru compilarea.
În teorie, cel mai probabil condiție ar trebui să se facă controlul sari cât mai puțin posibil.
De obicei cel mai probabil condiție ar trebui să fie în primul rând:
if (most_likely) {
// most likely instructions
} else …
Cele mai populare așm se bazează pe ramuri condiționată de faptul că sari atunci când condiția este adevărat. Care C cod va fi probabil tradus în astfel de pseudo asm:
jump to ELSE if not(most_likely)
// most likely instructions
jump to end
ELSE:
…
Acest lucru este din cauza salturi face cpu anula executarea conductei și stand pentru contor de program s-a schimbat (pentru arhitecturi care susține conducte care sunt foarte frecvente). Apoi e vorba de compilator, care pot sau nu se pot aplica unele sofisticate optimizări despre a avea din punct de vedere statistic, cel mai probabil, condiție pentru a obține controlul a face mai puțin sare.
Am decis să rulați din nou testul pe propria mea mașină, folosind Lik32 cod. A trebuit să-l schimbe din cauza windows-ul meu sau compiler gândire înaltă rezoluție este de 1ms, folosind
mingw32-g++.exe -O3 -Perete -std=c++11 -fexceptions -g
vector<int> rand_vec(10000000);
GCC a făcut aceeași transformare pe ambele codurile originale.
Rețineți că numai primele două condiții sunt testate ca cel de-al treilea trebuie să fie întotdeauna adevărat, GCC este un fel de Sherlock aici.
Invers
.L233:
mov DWORD PTR [rsp+104], 0
mov DWORD PTR [rsp+100], 0
mov DWORD PTR [rsp+96], 0
call std::chrono::_V2::system_clock::now()
mov rbp, rax
mov rax, QWORD PTR [rsp+8]
jmp .L219
.L293:
mov edx, DWORD PTR [rsp+104]
add edx, 1
mov DWORD PTR [rsp+104], edx
.L217:
add rax, 4
cmp r14, rax
je .L292
.L219:
mov edx, DWORD PTR [rax]
cmp edx, 94
jg .L293 // >= 95
cmp edx, 19
jg .L218 // >= 20
mov edx, DWORD PTR [rsp+96]
add rax, 4
add edx, 1 // < 20 Sherlock
mov DWORD PTR [rsp+96], edx
cmp r14, rax
jne .L219
.L292:
call std::chrono::_V2::system_clock::now()
.L218: // further down
mov edx, DWORD PTR [rsp+100]
add edx, 1
mov DWORD PTR [rsp+100], edx
jmp .L217
And sorted
mov DWORD PTR [rsp+104], 0
mov DWORD PTR [rsp+100], 0
mov DWORD PTR [rsp+96], 0
call std::chrono::_V2::system_clock::now()
mov rbp, rax
mov rax, QWORD PTR [rsp+8]
jmp .L226
.L296:
mov edx, DWORD PTR [rsp+100]
add edx, 1
mov DWORD PTR [rsp+100], edx
.L224:
add rax, 4
cmp r14, rax
je .L295
.L226:
mov edx, DWORD PTR [rax]
lea ecx, [rdx-20]
cmp ecx, 74
jbe .L296
cmp edx, 19
jle .L297
mov edx, DWORD PTR [rsp+104]
add rax, 4
add edx, 1
mov DWORD PTR [rsp+104], edx
cmp r14, rax
jne .L226
.L295:
call std::chrono::_V2::system_clock::now()
.L297: // further down
mov edx, DWORD PTR [rsp+96]
add edx, 1
mov DWORD PTR [rsp+96], edx
jmp .L224
Deci, acest lucru nu't ne spune prea multe, cu excepția faptului că ultimul caz nu't nevoie de o ramură a prezice.
Acum am incercat toate cele 6 combinații de cazul's, primele 2 sunt originale inversă și sortate. mare este >= 95, low este < 20, mijlocul este 20-94 cu 10000000 de iterații fiecare.
high, low, mid: 43000000ns
mid, low, high: 46000000ns
high, mid, low: 45000000ns
low, mid, high: 44000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 44000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 45000000ns
high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 42000000ns
mid, low, high: 46000000ns
high, mid, low: 46000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 43000000ns
high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 44000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 43000000ns
mid, low, high: 48000000ns
high, mid, low: 44000000ns
low, mid, high: 44000000ns
mid, high, low: 45000000ns
low, high, mid: 45000000ns
high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 43000000ns
mid, low, high: 46000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 45000000ns
low, high, mid: 44000000ns
high, low, mid: 42000000ns
mid, low, high: 46000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 45000000ns
low, high, mid: 44000000ns
1900020, 7498968, 601012
Process returned 0 (0x0) execution time : 2.899 s
Press any key to continue.
Deci, de ce este ordinea high, low, med, apoi mai repede (marginal)
Pentru că cel mai imprevizibil este ultima și, prin urmare, nu este niciodată a alerga printr-un branch predictor.
if (i >= 95) ++nHigh; // most predictable with 94% taken
else if (i < 20) ++nLow; // (94-19)/94% taken ~80% taken
else if (i >= 20 && i < 95) ++nMid; // never taken as this is the remainder of the outfalls.
Deci, ramurile vor fi prezis luat, luat și restul cu
6%+(0.94*)20% mispredicts.
"Rezolvat"
if (i >= 20 && i < 95) ++nMid; // 75% not taken
else if (i < 20) ++nLow; // 19/25 76% not taken
else if (i >= 95) ++nHigh; //Least likely branch
Ramurile vor fi prezis cu nu luate și Sherlock.
25%+(0.75*)24% mispredicts
Da 18-23% diferența (măsurată diferența de ~9%), dar avem nevoie pentru a calcula cicluri în loc de mispredicting %.
Las's presupunem 17 cicluri mispredict pedeapsa pe Nehalem CPU și că fiecare control are 1 ciclu pentru problema (4-5 instrucțiuni) și bucla are un ciclu prea. Datele dependențele sunt contoare și bucla variabile, dar odată ce mispredicts sunt din cale nu ar trebui't influența momentul.
Deci, pentru "invers", vom obține timpii (acest lucru ar trebui să fie formula utilizată în Arhitectura calculatoarelor: O Abordare Cantitativă IIRC).
mispredict*penalty+count+loop
0.06*17+1+1+ (=3.02)
(propability)*(first check+mispredict*penalty+count+loop)
(0.19)*(1+0.20*17+1+1)+ (= 0.19*6.4=1.22)
(propability)*(first check+second check+count+loop)
(0.75)*(1+1+1+1) (=3)
= 7.24 cycles per iteration
și la fel pentru "rezolvat"
0.25*17+1+1+ (=6.25)
(1-0.75)*(1+0.24*17+1+1)+ (=.25*7.08=1.77)
(1-0.75-0.19)*(1+1+1+1) (= 0.06*4=0.24)
= 8.26
(8.26-7.24)/8.26 = 13.8% vs ~9% măsurată (aproape de măsurat!?!).
Deci, evident de la OP nu este evident.
Cu aceste teste, alte teste cu codul mai complicat sau mai multe date dependențele vor fi cu siguranță diferite, astfel încât măsura caz.
Schimbarea ordinii de testare a schimbat rezultate, dar care ar putea fi cauza diferite aliniamente de buclă de început, care ar trebui să fie în mod ideal 16 bytes aliniate pe toate mai noi Procesoare Intel, dar nu't în acest caz.
Pune-le în orice ordine logică vă place. Sigur, sucursala poate fi mai lent, dar de ramificare nu ar trebui să fie majoritatea de muncă computer este de a face.
Dacă sunteți de lucru pe o performanță critică porțiune de cod, atunci cu siguranță utilizarea ordine logică, profil ghid de optimizare și alte tehnici, dar pentru cod general, cred ca e mai mult o alegere de stil.
Dacă știți deja probabilitatea relativă de if-else declarație,apoi pentru performanță scop ar fi mai bine să folosiți sortate, așa cum se va verifica numai cu o singură condiție(cel adevărat).
Într-un nesortate mod compilatorul va verifica toate condițiile inutil și va lua timp.