Optimizarea codului programului. Principalele caracteristici ale optimizării codului de către programator și compilator

Uneori poate fi dificil să decizi care construct este mai bine să folosești i++ sau ++i sau să alegi între if-else și switch. Acest articol, scris special pentru comunitatea iT works, prezintă cele mai reale instrumente de optimizare a codului pe care ar trebui să le cunoască orice programator profesionist.

Unii oameni cred că zilele optimizării la nivel de cod au dispărut pentru totdeauna, dar acest lucru nu este adevărat. Acum există multe platforme în care nu există compilatoare atât de puternice ca în Microsoft Visual Studio. De exemplu, limbaje shader (hlsl, glsl) sau cod pentru CUDA, PlayStation3, SPU sau platforme mobile. În funcție de organizarea codului, eficiența acestuia poate diferi de zece ori, uneori din cauza ineficienței compilatorului, dar mai des din cauza accesului la memorie.

Când programați pentru diferite platforme, studiați capacitățile compilatorului și caracteristicile arhitecturii procesorului (dacă scrieți pentru o anumită consolă). Efectuați teste de performanță ale diferitelor opțiuni de optimizare. Este adesea dificil de ghicit care metode vor fi cele mai eficiente. Și acest articol vă va spune diferite trucuri care vă pot ajuta. Cu toate acestea, nu ar trebui să optimizați orbește, fără analiză și profilare prealabilă. Amintiți-vă că optimizarea prematură este un rău.

Dacă sunteți programator în VS sub Windows, atunci cel mai probabil compilatorul va face față eficient cu multe dintre tehnicile de optimizare descrise. Acordați atenție punctelor de lucru cu memorie și vă recomand, de asemenea, să vă familiarizați cu tehnica proiectare orientată către date. Căutați câteva sfaturi despre utilizarea acestuia în articol.

Deci, să începem:

1. Folosiți instrucțiuni de vectorizare a datelor și de procesare vectorială (de exemplu, SSE în CPU sau pack de date dacă utilizați shaders sau CUDA). Acest lucru va permite utilizarea arhitecturii SIMD (Single Instruction, Multiple Data), care va crește semnificativ viteza de calcul. Dacă decideți să utilizați această metodă, atunci nu uitați de alinierea datelor în memorie.

2. Este mai eficient să adăugați și să înmulțiți într-un amestec decât să adăugați mai întâi totul și apoi să înmulțiți totul. Acest lucru se datorează faptului că adunarea și înmulțirea sunt efectuate de diferite module de procesor și pot fi efectuate simultan.
int a,b,c,k,m,n,t,f1,f2,f3,g1,g2,g3; a = b + c; k = m + n; t = a + k; f1 = f2 * f3; g1 = g2 * g3; Mai puțin eficient decât: a = b + c; f1 = f2 * f3; k = m + n; g1 = g2 * g3; t = a + k;

3. Nu există nicio diferență de viteză când se lucrează cu float și dublu când se adună și se înmulțește. Se execută în același număr de cicluri de procesor și folosesc aceleași registre. La împărțirea și luarea rădăcinii, plutirea este mai rapidă. Cu toate acestea, dacă utilizați cantități mari de date, atunci datorită cache-ului, tipul pe care îl ocupă mai putina memorie(adică float), așa că, în general, este de preferat să-l folosești. Alegerea dublu are sens atunci când este nevoie de mai multă precizie.

4.
Hai sa mancam. const float a = 100,0f; float some1 = some3 * 1.0f/a; float some2 = some4 * 1.0f/a; este mai eficient să scrii not: const float a_inv = 1.0f / a; oare1 = oare3 * a_inv; oare2 = oare4 * a_inv; și așa: oare1 = oare3 * (1.0f / a); oare2 = oare4 * (1.0f/a); De ce ar fi mai eficient? Operatorii cu prioritate egală sunt executați secvențial. Aceasta înseamnă că mai întâi se va efectua înmulțirea, apoi împărțirea. Dacă încadrați operația de împărțire între paranteze, atunci compilatorul o va executa și în timp real va fi efectuată doar operația de înmulțire. În ceea ce privește diferențele dintre opțiunea 3 și opțiunea 2, în a 3-a opțiune nu este creată o variabilă suplimentară, nu este nevoie să ne gândim la ce este această variabilă când privim codul. Și eficacitatea opțiunii a 2-a și a 3-a va fi aceeași.

5. Pe cantități mari de date și calcule pe acestea, float este mai profitabil decât dublu (din cauza erorilor de cache, vezi punctul 3).

6.
a, b - orice expresie Func1, Func2 - funcții care vor fi apelate pentru a calcula condiția if(a && b) - trebuie să setați prima condiție mai puțin probabilă, pentru o eficiență mai mare dacă (a || b) - trebuie să setați mai întâi condiția cea mai probabilă, pentru mai eficient if(Func1() && Func2()) - trebuie să puneți mai întâi operatorul mai rapid

7.
void Func(int* a) ( int b = 10; Următoarele linii sunt la fel de eficiente (din punct de vedere al timpului de execuție): b = 100; *a = 100; ) Acest lucru se întâmplă deoarece variabila stivă este accesată de pointerul stivei. Există, de asemenea, o dereferire a indicatorului.

8. Dacă există o gamă largă de structuri, atunci trebuie să faceți dimensiunea elementelor sale egală cu puterea a doi. Apoi trecerea printr-o astfel de matrice va fi mult mai rapidă (de 4-6 ori), datorită alinierii pointerului la fiecare structură din matrice.

9.
int a[ 1000 ]; for(int i =0; i<1000; ++i) a[ i ] = 50; Значительно эффективнее будет: int* p = a; for(int i =0; i<1000; ++i, ++p) *p = 50;

10.
SomeClass*p; - pointer către o matrice de elemente x = *(p++); - mult mai eficient x = *(++p); Din același motiv ca și punctul 1. În primul caz, indicatorul va fi dereferențiat și incrementat în paralel, iar în al doilea caz, secvenţial.

11. Numărul de coloane dintr-o matrice bidimensională ar trebui să fie de preferință egal cu o putere a două. Acest lucru va crește viteza de lucru cu matricea. Acest lucru va alinia pointerii la primele elemente ale fiecărui rând, ceea ce va accelera accesul la elemente.
int mas[ 10 ][ 16 - numărul de coloane ar trebui să fie de preferință o putere de două]

12.
u32a; f32b; b = (f32)(i32)a; - mai rapid b = (f32)a;

13. Evitați turnarea tipului.
plutitor f; int a; float b = (plutitor)a; - long int m = (int)f; - un timp îndelungat

14. Folosiți cu înțelepciune operațiunile de rotunjire:
numai pentru nesemnat: (u32)x este de 10 ori mai rapid decât „u32(floor(x))” u32(x + 1,0f) este de 10 ori mai rapid decât „u32(celula(x))” u32(x + 0,5f) sunt de 10 ori mai rapide decât „u32(round(x))”
15.
float f = 1,0f; *(int*)&f ^= 0x80000000; - mai rapid decât f *= -1,0f;

16. Dacă valorile secvențiale ale parametrilor cazului sunt utilizate în comutare (cazul 0: cazul 1: cazul 2:...), atunci comutatorul este mult mai eficient decât if-else. Acest lucru se datorează faptului că, cu if-else, valoarea fiecărei condiții va fi calculată, iar în cazul unor astfel de parametri din constructia comutatorului, valoarea va fi calculată o dată, iar apoi va avea loc o tranziție imediată la valoarea dorită. articol.

17. Ramificarea este rău. Încercați să reduceți numărul lor. Nu le faceți în bucle mari. comutatorul este, de asemenea, o ramură. Procesorul încearcă să prezică rezultatul condiției (predicția ramurilor), iar dacă valoarea expresiei este aproape întotdeauna aceeași, atunci ramificarea nu va afecta viteza de execuție a codului. Cu toate acestea, în general, predicția ramurilor va fi greșită în 50% din timp, ceea ce va încetini algoritmul. Fiecare ramură este o tranziție la o secvență de instrucțiuni de procesor. O astfel de tranziție rupe conducta de instrucțiuni a procesorului și este destul de costisitoare.

Acest lucru este valabil mai ales pentru shadere, subrutine SPU, subrutine CUDA și în algoritmi în care o cantitate mare de date este procesată. Dacă trebuie să executați un cod pentru o sută de mii de particule, atunci încercați să minimizați numărul de ramuri. Acest lucru poate accelera semnificativ execuția codului.

Const int NN = 12500000; const int N = 10; următorul lucru este rău (200 ms pe mașina mea): for(int i = 0; i< NN; ++i) { switch(i % N) { case 0: res += 10; break; case 3: res += 30; break; case 5: res += 50; break; case 6: res += 60; break; case 8: res += 80; break; } } гораздо лучше (120 ms на моей машине): const int arr = { 10, 0, 0, 30, 0, 50, 60, 0, 80, 0 }; for(int i = 0; i < NN; ++i) res += arr[ i % N ];

18. Luați în considerare un exemplu. Un sprite 2D conține o matrice de vârf [4]. Ar fi mult mai eficient să se facă o singură stocare de vârfuri, iar în sprite indicele de offset relativ la primul element.
Acest lucru va economisi 16 octeți pentru fiecare sprite din punct de vedere al memoriei, iar din punct de vedere al vitezei, trecerea prin vârfuri va fi cu 30% mai rapidă. Acesta este un design orientat către date. Este același lucru pentru C#.

Principalele domenii de optimizare:
1. Reducerea numărului de ramuri
2. Gruparea datelor după aceleași tipuri în memorie (în C#, nimeni nu a anulat încă matrice de structuri)
3. Reducerea dimensiunilor structurilor

19.funcții inline:
+ oferă un câștig de viteză
- mărește codul
- adaugă dependențe (fișiere *.h) la cod la compilare. Acest lucru mărește timpul și volumul de compilare atunci când se schimbă codul într-o funcție

Date:
1. Este posibil ca compilatorul să nu integreze funcția (chiar _forceinlie - nu există nicio garanție de inline)
2. Când optimizarea vitezei este activată, compilatorul VS inlinează orice funcții la discreția sa, chiar dacă acestea nu sunt declarate ca inline.

Concluzie: ar trebui să evitați utilizarea funcțiilor inline. Acum acest lucru nu este necesar. O excepție poate fi doar codul de nivel scăzut executat foarte frecvent (cum ar fi funcțiile matematice) sau dacă scrieți un program folosind un compilator fără optimizare completă a codului (de exemplu, folosirea inline pentru compilatorul PlayStation3 este încă relevantă).

20. Luați în considerare rezultatul modificării ordinii variabilelor din structură.
struct s1 ( short int a; double b; int d; ) sizeof(s1[ 10 ]) == 24 * 10 struct s2 ( double b; int d; short int a; ) sizeof(s1[ 10 ]) == 16 * 10 dublu cu 8 este întotdeauna aliniat

21. Prin rearanjarea locurilor termenilor pentru valoarea flotantă, suma se modifică:
1e+8f + 1,23456 - 1e+8f == 0 dar 1e+8f - 1e+8f + 1,23456 == 1,23456

22. Căutarea binară nu trebuie utilizată pentru un număr mic de articole. Dacă numărul de elemente este mai mic de 40-60 (acest număr poate varia de la implementarea algoritmilor, dar este cam în această ordine), căutarea binară va fi mai lentă decât cea liniară.

23.
Este posibil astfel: bool b; int a = b ? X y; Dar mai rapid: int b; (0 - fals, -1 - adevărat) int a = (x & b) | (y & ~b);

24.
int a, b; 1. int x = (a >= b ? 1: 0); 2. int x = (a >= b ? -1: 0); Poate fi înlocuit cu: 1. int x = (b - a) >> 31; 2. int x = (b - a) & 0x80000000;

25.
i32 iIndex; Condiție: if(iIndex< 0 && iIndex >= iSize) Poate fi înlocuit cu acesta: if((u32)iIndex >= iSize) Condiție: if(i >= min && i<= max) Можно заменить таким: if((u32)(i-min) <= (u32)max - min)

26. Mai sus a fost un exemplu despre cum comutatorul poate fi transformat într-o matrice const static și accesat prin index. Acest lucru este aplicabil, de exemplu, pentru rtti (identificarea tipului de timp de rulare). Dacă comutatorul indicator al funcției este definit în acest fel, atunci înlocuirea acestuia cu un acces în timp constant la funcția dorită poate fi extrem de utilă. Același lucru este adevărat dacă este o mașină de stat. În loc să adăugați un nou element la comutator, acesta poate fi adăugat la matricea de mai sus. Dar amintiți-vă punctul 16.

int func(int index) ( switch(index) ( caz 0: return f_Func1(); caz 3: return f_Func2(); caz 4: return f_Func2(); .. case .. return f_FuncN(): ) return 0; ) înlocuiți cu: int func(int index) ( static funcPtr array = ( &f_Func1, NULL, &f_Func2, ... &f_FuncN ) return array[ index ](); )

În plus

Pentru mai multe sfaturi despre scrierea unui cod mai eficient, consultați articolele:

Un pic despre trist: întreaga noastră viață este o luptă cu frâne. Și nu poate continua așa pentru totdeauna. Este necesar să optimizați totul - de la locul de muncă până la timp. În acest articol, am dat exemple de optimizare a codului în limbajul de programare Delphi, dar credeți-mă, aceste sfaturi vă pot fi utile în viața reală, dacă vă gândiți bine.

1. Aproape totul poate fi optimizat. Și chiar și acolo unde ți se pare că totul funcționează rapid, o poți face și mai repede. Trebuie amintit că orice problemă poate fi rezolvată în mai multe moduri, iar sarcina ta este să o alegi pe cea mai rațională dintre ele.

2. Optimizarea ar trebui să înceapă întotdeauna cu punctele slabe din codul programului. De obicei, nu este nevoie să optimizați ceva care funcționează atât de repede. Iar efectul unei astfel de optimizări va fi minim.

3. La optimizare, trebuie să analizați toate operațiunile, fiecare operator, fără a rata nimic. De obicei, optimizarea începe din acele locuri din cod în care există operații, cicluri repetate în mod regulat. Ceea ce se află în buclă va fi repetat de n număr de ori, deci cu cât este mai puțin cod în buclă, cu atât procesorul îl va calcula mai repede. Dacă ciclul se dovedește a fi prea mare, acesta poate fi descompus în mai multe altele mai mici. În acest caz, dimensiunea programului nostru va crește, dar viteza va crește.

4. Încercați să utilizați mai puține calcule cu virgulă mobilă. Orice operații cu numere întregi sunt efectuate cu un ordin de mărime mai rapid. Operațiile de înmulțire sau împărțire durează, de asemenea, mult timp. În loc de înmulțire, este mai bine să folosiți adunarea, iar împărțirea poate fi înlocuită cu o schimbare. Schimbarea este mult mai rapidă decât înmulțirea și împărțirea. Acest lucru se datorează faptului că toate numerele sunt stocate în binar. Dacă convertiți un număr din zecimal în binar și mutați numărul la dreapta cu o poziție, veți observa că această operație este similară cu împărțirea la 2. Când treceți la stânga, numărul este împărțit la 2. Deși aceste operații sunt similar, schimbarea funcționează de câteva ori mai repede.

5. Când creați proceduri, nu le împovărați cu un număr mare de parametri de intrare. Și totul pentru că cu fiecare apel la procedură, parametrii acesteia sunt ridicați într-o zonă specială de memorie, stiva, iar după ieșire, aceștia sunt eliminați de acolo. De asemenea, este necesar să acționați cu atenție și cu parametrii înșiși. Nu este necesară trimiterea către proceduri a variabilelor care conțin datele de volum mare în formă pură. Este mai bine să treceți adresa celulei de memorie în care sunt stocate datele, iar în cadrul procedurii să lucrați deja cu această adresă.

6. În momentele cele mai critice ale programului, cum ar fi ieșirea pe ecran, puteți utiliza limbajul Assembler. Chiar și asamblatorul încorporat al Delphi este mult mai rapid decât funcțiile în limba maternă. Codul de asamblare poate fi extras într-un modul separat, compilat și conectat la programul dumneavoastră.

7. Nu există verificări inutile. Nu vă gândiți că dacă nu aveți un fel de situație non-standard, atunci nu va apărea nici pentru utilizator. Validați întotdeauna ceea ce introduce utilizatorul fără să așteptați ca intrarea să fie necesară.

8. Dacă scrieți un program destul de mare și greoi, adăugați comentarii la el. Compilatorul încă le ignoră. Și dacă dintr-o dată doriți să vindeți codurile sursă ale programelor dvs., comentariile le vor crește prețul și vă va fi mai ușor să le navigați singur.

9. Pentru a obține un efect bun, trebuie să cunoașteți IDE-ul, mediul de dezvoltator integrat, limbajul în care programați, în cazul nostru Delphi. De obicei, opțiunile IDE vă permit să alegeți diferite tipuri de compilatoare, iar implicit este cea mai simplă, cea mai rapidă compilare, dar creând cod mai puțin optimizat. Prin urmare, puneți întotdeauna cel mai optimizat tip de compilator.

10. Încercați să faceți programele să aibă o interfață standard. Ei bine, nu este nevoie să faceți butoane triunghiulare, meniuri non-standard și alte clopote și fluiere grafice. Toate acestea încetinesc foarte mult programul, consumă o cantitate mare de resurse computerizate și necesită timp suplimentar de dezvoltare. De exemplu, UNIX real este în general un shell obișnuit - o linie pentru introducerea comenzilor.

E ca totul. Îți doresc mult succes la scrierea programelor tale, trebuie doar să urmezi aceste sfaturi și vei reuși.

Și îmbunătățirea eficienței. Obiectivele de optimizare includ reducerea cantității de cod, a cantității de RAM utilizată de program, accelerarea programului și reducerea numărului de operațiuni I/O.

Principala cerință care se face de obicei metodei de optimizare este ca programul optimizat să aibă același rezultat și efecte secundare asupra aceluiași set de date de intrare ca și programul neoptimizat. Cu toate acestea, această cerință poate să nu joace un rol special dacă câștigul din utilizarea optimizării poate fi considerat mai important decât consecințele schimbării comportamentului programului.

Tipuri de optimizare

Optimizarea codului poate fi efectuată atât manual, de către un programator, cât și automat. În acest din urmă caz, optimizatorul poate fi fie un instrument software separat, fie încorporat în compilator (așa-numitul compilator de optimizare). În plus, trebuie remarcat faptul că procesoarele moderne pot optimiza ordinea în care sunt executate instrucțiunile de cod.

Există concepte precum optimizarea la nivel înalt și la nivel scăzut. Optimizările de nivel înalt sunt realizate în mare parte de un programator, care, operând cu entități abstracte (funcții, proceduri, clase etc.) și imaginând un model general pentru rezolvarea unei probleme, poate optimiza proiectarea sistemului. Optimizările la nivelul blocurilor structurale elementare ale codului sursă (bucle, ramuri etc.) sunt, de obicei, denumite la nivel înalt; unii le evidențiază ca un nivel separat („mijloc”) (N. Wirth?). Optimizarea la nivel scăzut este efectuată în etapa de transformare a codului sursă într-un set de instrucțiuni ale mașinii și adesea această etapă este supusă automatizării. Cu toate acestea, programatorii în limbaj de asamblare cred că nicio mașină nu poate depăși un programator bun în acest sens (în timp ce toată lumea este de acord că un programator prost va înrăutăți mașinile).

Selectarea zonei de optimizat

Când optimizați manual codul, există o altă problemă: trebuie să știți nu numai cum să optimizați, ci și unde să îl aplicați. De obicei, din cauza diverșilor factori (operații lente de introducere, diferența de viteză a operatorului uman și a mașinii etc.), doar 10% din cod durează până la 90% din timpul de execuție (desigur, declarația este mai degrabă speculativ, și are o bază dubioasă sub forma unei legi Pareto, totuși, pare destul de convingător în E. Tanenbaum). Deoarece va trebui să se aloce timp suplimentar pentru optimizare, prin urmare, în loc să încercați să optimizați întregul program, ar fi mai bine să optimizați aceste 10% „critice” din timpul de execuție. O astfel de bucată de cod se numește blocaj sau blocaj și, pentru a o determina, se folosesc programe speciale - profilere care vă permit să măsurați timpul diferitelor părți ale programului.

De fapt, în practică, optimizarea se face adesea după faza de programare „haotică” (care implică lucruri precum „”, „ne vom da seama mai târziu”, „o vom face oricum”), deci este un amestec de optimizare, refactorizare și reparare: constructe de simplificare „fancy” precum strlen(path.c_str()), condiții booleene (a.x != 0 && a.x != 0), etc. Profilele nu sunt potrivite pentru astfel de optimizări. Cu toate acestea, pentru a detecta astfel de locuri, puteți utiliza programe - instrumente pentru găsirea erorilor semantice bazate pe o analiză profundă a codului sursă - la urma urmei, după cum puteți vedea din al doilea exemplu, codul ineficient poate fi rezultatul unor erori (cum ar fi greșeli de scriere în acest exemplu - cel mai probabil, a.x != 0 && a.y != 0). Unul bun va detecta un astfel de cod și va afișa un mesaj de avertizare.

Daune și beneficii ale optimizărilor

Aproape totul în programare ar trebui tratat rațional, iar optimizările nu fac excepție. Se crede că un programator de asamblare fără experiență scrie de obicei cod care este de 3-5 ori mai lent decât codul generat de compilator (Zubkov). Există o expresie binecunoscută despre optimizările timpurii, destul de la nivel scăzut (cum ar fi lupta pentru un operator suplimentar sau o variabilă), formulată de Knuth: „Optimizarea prematură este rădăcina tuturor problemelor”.

Majoritatea oamenilor nu au nicio plângere cu privire la optimizările efectuate de optimizator și, uneori, unele optimizări sunt practic standard și obligatorii - de exemplu, optimizarea recursiunii cozii în limbaje funcționale (recursiunea cozii este un anumit tip de recursivitate care poate fi redusă la forma unui ciclu).

Cu toate acestea, trebuie înțeles că numeroase optimizări complexe la nivel de cod de mașină pot încetini foarte mult procesul de compilare. Mai mult, câștigul din acestea poate fi extrem de mic în comparație cu optimizările designului general al sistemului (Wirth). De asemenea, nu trebuie uitat că limbajele moderne, sintactic și semantic „fanteziste” au multe subtilități, iar un programator care nu le ia în considerare poate fi surprins de consecințele optimizării.

De exemplu, luați în considerare limbajul C++ și așa-numitul. Optimizarea valorii returnate, a cărei esență este că compilatorul nu poate crea copii ale obiectului temporar returnat de funcție. Deoarece compilatorul „sări” copia în acest caz, acest truc se mai numește și „Copy elision”. Deci următorul cod:

#include struct C ( C() () C(const C&) ( std::cout<< "A copy was made.\n"; } }; C f() { return C(); } int main() { std::cout << "Hello World!\n"; C obj = f(); }

poate avea mai multe opțiuni de ieșire:

Salut Lume! S-a făcut o copie. S-a făcut o copie. Salut Lume! S-a făcut o copie. Salut Lume!

În mod ironic, toate cele trei opțiuni sunt legale, deoarece standardul de limbaj permite omiterea apelului constructorului de copiere în acest caz, chiar dacă constructorul are efecte secundare (§12.8 Copierea obiectelor clasei, clauza 15).

Rezultat

Astfel, nu uitați să optimizați codul, folosind instrumente software specializate dacă este posibil, dar acest lucru ar trebui făcut cu atenție și cu prudență și, uneori, să vă pregătiți pentru surprize de la compilator.

PVS Studio

Lista bibliografică

  • E. Tanenbaum. Arhitectura calculatorului.
  • Wirth N. Construirea compilatoarelor.
  • Knut D. Arta programarii, Volumul 1. Algoritmi de baza.
  • Zubkov S.V. Asamblator pentru DOS, Windows și UNIX.
  • Wikipedia. optimizarea codului.
  • Wikipedia.

În timpul dezvoltării inițiale a site-ului, proprietarii lor acordă cea mai mare atenție percepției sale externe și lansării rapide. Imediat sau la câteva luni de la lansare, apare întrebarea cum să atrageți mai mulți clienți. După ceva timp, designerul de layout și programatorul vin să lucreze la optimizarea internă a site-ului, unde se dovedește că o parte din codul scris trebuie rescrisă. Prin urmare, în această postare vom vorbi despre optimizarea codului html, css și js al site-ului în timpul dezvoltării sale inițiale, ceea ce va permite clientului să economisească bani și dezvoltatorilor să se pună pe nervi.

optimizarea js și css

Să începem cu css și js. Pentru ce este optimizarea css și js?

Aproximativ 50% dintre utilizatori părăsesc site-ul dacă durează mai mult de 3 secunde pentru a se încărca, iar cu fiecare secundă în plus, conversia site-ului scade cu 7%. De asemenea, viteza de încărcare a site-ului este unul dintre factorii de clasare.

Primul lucru pe care trebuie să începeți este să urmați recomandările Google. Codul Css și js nu trebuie să fie conținut în codul html al site-ului, acesta trebuie plasat în fișiere separate. Excepție fac stilurile mici în linie cu 1-2 valori. Numărul de fișiere incluse ar trebui redus cât mai mult posibil, lăsând în mod ideal câte un fișier css și js inclus fiecare. Conexiunea fișierelor js trebuie mutată la sfârșitul paginii (înainte de afișarea paginii, browserul trebuie să o analizeze și dacă detectează un script extern, trebuie să-l încarce, iar acesta este un ciclu suplimentar de operațiuni care încetinește afișarea paginii.

De asemenea, pentru a accelera încărcarea fișierelor js, css și a imaginilor, este de dorit să folosiți cache și compresie în format GZIP.

Aspect SEO pentru site-ul web: optimizarea codului html sau cum să îl faceți astfel încât să nu trebuiască să îl repetați mai târziu

Pentru o viitoare optimizare corectă a codului html, să ne uităm la toate etichetele și la modul în care acestea afectează SEO.

bloc :

- indică numele paginii, care este plasat în fila browser și în motoarele de căutare. Cea mai importantă etichetă în ceea ce privește influențarea clasamentului site-ului.</p> <p><description>- vă permite să setați o descriere a paginii care apare în rezultatele căutării sub titlu. Are un impact mult mai mic asupra clasamentelor, dar ajută la creșterea CTR (raportul de clic la afișări) al paginii. Dacă eticheta meta description este completată, acest lucru nu garantează că rezultatele vor arăta exact ce este scris acolo, deoarece motoarele de căutare pot prelua descrierea din conținut. Dar totuși, este mai bine să configurați generarea de etichete și să nu vă gândiți la ce parte a textului PS va fi luată pentru descriere.</p> <p><keywords>- indică motoarele de căutare, pentru care interogă pagina relevantă. După introducerea acestei etichete, i s-a dat o mare influență asupra clasamentului paginilor. Optimizatorii ar putea promova cu ușurință o pagină cu orice produs al unui magazin online, de exemplu, la solicitarea „descărcați un abstract de poveste” sau pe alte subiecte care au adus vizitatori pe site, dar nu și clienți. Acum influența acestei etichete asupra promovării nu este cunoscută cu exactitate și mulți oameni pur și simplu o ignoră, inclusiv pentru a nu dăuna paginii.</p> <p><meta name="robots" content="index/noindex, follow/nofollow">(una dintre valori este luată, index sau noindex, follow sau nofollow) — interdicția de indexare a paginii (noindex) și interdicția de indexare a linkurilor de ieșire pe pagină (nofollow) de către motoarele de căutare. Valorile de index și de urmărire sunt utilizate împreună cu valorile fără indexare, deoarece indexarea paginilor și a linkurilor este permisă în mod implicit. Utilizați această etichetă cu atenție pentru a nu vedea trafic zero de la motoarele de căutare după un timp.</p> <p><link rel="canonical" href="..." />- vă permite să legați mai multe pagini identice în conținut, dar cu adrese URL diferite, la o singură pagină pentru a-și îmbunătăți clasarea. În cele mai multe cazuri, este folosit pentru pagini dinamice care conțin același conținut, de exemplu, sortarea paginilor dintr-un catalog de produse sau atunci când lucrați cu un blog, unde un articol poate fi în secțiuni diferite și poate avea adrese URL diferite.</p> <p><link rel="prev" href="..." />Și <link rel="next" href="..."/>- etichetele vă permit să specificați paginile anterioare și următoare pentru motoarele de căutare pe paginile de paginare, dacă materialul este împărțit în mai multe părți și se află pe adrese URL diferite.</p> <h3>bloc <body> :</h3> <p><h1> - <h6>- titluri de pe pagină. Etichetă <h1>ar trebui folosit o dată, ca <title>indică conținutul principal al paginii, dar are un efect mai mic asupra clasamentului în SERP. De regulă, pentru magazinele online în etichetă <h1>denumirile acestor categorii si produse sunt indicate pe paginile de categorii si produse, pentru paginile de informare - numele care va interesa cititorul, plus, daca este posibil, cuvinte cheie.</p> <p>Etichete <h1> - <h6>trebuie să urmeze o structură logică. antet <h1>conțin anteturi <h2>, în care titlurile <h3>etc. Este indicat să le folosiți doar în conținutul text al paginii (de exemplu, pentru a sparge conținutul principal al paginii, dar nu și pentru blocurile care sunt afișate pe toate paginile site-ului). Având în vedere că eticheta <h1>ajută la creșterea valorii cuvintelor în clasament, închideți tot textul de pe site în el și reparați-l cu ajutorul stilurilor, astfel încât să poată fi citite, atunci acest lucru nu va oferi niciun avantaj, ci doar va dăuna unei astfel de pagini.</p> <p><strong>, <b>, <em>- conceput pentru a se concentra asupra anumitor fraze și cuvinte din descrierea unei pagini, articol, știri etc. (inclusiv creșterea importanței acestor cuvinte la clasare). Nu ar trebui să le folosiți pentru aspectul acelor elemente de pagină care se repetă, de exemplu, pe toate produsele. Pentru aceasta este mai bine să folosiți css. Deși nu se știe cu siguranță dacă un cuvânt sau o expresie repetată pe toate paginile site-ului are efect, în interiorul, de exemplu, a unei etichete <strong>, dar este mai bine să folosiți etichetele pentru scopul propus. Cred că PS va aprecia.</p> <p><table>- destinat, de asemenea, în primul rând plasării în conținutul text al paginii. Vă permite să faceți textul mai interesant de citit, ceea ce crește credibilitatea întregii pagini din motoarele de căutare (listele, imaginile, videoclipurile au același efect).</p> <p><ul>, <li>, <ol>, <dl>, <dd>, <dt>- liste care sunt folosite pentru crearea meniului site-ului și în partea principală a paginii pentru structurarea informațiilor textuale.</p> <p><img>- poze pe pagina. Descrierea imaginii trebuie plasată în atributele alt=”...” și title=”...”, ceea ce va ajuta la clasarea în căutarea imaginilor. De asemenea, poziția imaginii în rezultatele căutării este afectată dacă numele fișierului imagine corespunde descrierii acestuia.</p> <p><noindex>- indică lui Yandex conținutul documentului care nu trebuie indexat, de exemplu, informații de serviciu. Trebuie folosit foarte atent și în cazuri rare.</p> <p><div>- eticheta reală pentru aspectul site-ului, nu afectează SEO.</p> <p>O etichetă pentru împachetarea textului, dar nu pentru modificarea plasării blocurilor. Dar aceasta este mai mult pentru validitatea aspectului, și nu pentru optimizare. Nu afectează optimizarea site-ului.</p> <p><p>Specifică un paragraf de text pentru conținutul principal de pe site (de exemplu, articole sau descrieri de produse, categorii dintr-un magazin online). De asemenea, este de dorit să se aplice în principal conținutului principal al unei singure pagini.</p> <p>Un element inline care nu are niciun efect asupra SEO. Este util în multe cazuri de utilizat împreună cu CSS în conținutul paginii non-principale pentru a înlocui etichetele de evidențiere și de titlu.</p> <p><header>- antetul site-ului.</p> <p><footer>subsolul site-ului.</p> <p><a>- aici avem nevoie de un articol separat. Și nu singur.</p> <p>Poate am ratat o etichetă... dar asta înseamnă că este mai puțin importantă. De asemenea, unele dintre noile etichete html5 nu au fost luate în considerare, cum ar fi <article> , <aside> , <nav> , <section> .</p> <p>Dacă aranjați etichete html, deoarece acestea influențează relevanța cuvintelor cheie, atunci va apărea undeva așa: title, h1-h6,strong, description, b, em, p, keywords, ul->li & ol->li.</p> <p>Acum, pentru o prezentare mai bună, să încercăm să creăm un aspect de pagină optimizat corespunzător.</p><p> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Titlul paginii

  • Categoria 1
  • Categoria 2
    • Categoria 2.1
    • Categoria 2.2
  • Categoria 3

Titlul paginii

Conținut principal cu etichete

-

, , , ,
, .

Bloc lateral cu informații suplimentare.
...


Ce altceva trebuie să iei în considerare atunci când SEO-layout-ul site-ului

  • Un număr mare de erori de validare pot avea un efect negativ asupra unei pagini. Nu este recomandabil să folosiți etichete goale și fișiere css, js care nu sunt folosite pe pagină. Cu cât codul este mai ușor, cu atât este mai ușor pentru motoarele de căutare să-l analizeze.
  • Nu ar trebui să utilizați flash și cadre, care nu sunt foarte prietenoase cu motoarele de căutare. De asemenea, motoarele de căutare nu recunosc textul care este desenat folosind o imagine.
  • Caracterul cross-browser al site-ului afectează comportamentul utilizatorilor și îi face să părăsească site-ul fără a primi informațiile necesare sau fără a efectua o achiziție. Ca urmare, factorii comportamentali care afectează optimizarea întregului site se înrăutățesc.
  • Prezența unei versiuni mobile a site-ului sau adaptabilitatea acesteia a devenit un factor de clasare și, la fel ca compatibilitatea între browsere, vă permite să reduceți rata de respingere și să creșteți conversia site-ului pe dispozitivele mobile. Google a început să ia în considerare prezența unei versiuni mobile în 2015 (compatibilă cu dispozitivele mobile), iar Yandex în 2016, numind algoritmul de clasare „Vladivostok”.
  • Conținutul principal al paginii ar trebui plasat în codul html mai aproape de început, deci va fi mai relevant din punctul de vedere al motorului de căutare.
  • Conținutul nu trebuie ascuns cu display:none .
  • Dacă utilizarea etichetelor poate crește importanța unui cuvânt cheie, atunci puteți obține și un efect negativ dacă unele etichete se suprapun, de exemplu
    1. h1-h6 & puternic, b, em
    2. h1-h6 & a href=…
    3. puternic, b, em & a href=…

Concluzie

Privind paginile motoarelor de căutare, puteți vedea o serie de erori asociate cu aspectul site-ului, inclusiv erori de validare. Dar aici trebuie să se înțeleagă că și-au propus obiective complet diferite. Aspectul optimizat SEO este necesar pentru site-uri, una dintre principalele surse de trafic ale cărora sunt motoarele de căutare și, oricât de grozave leagă linkurile către site, nu poți visa la primele poziții fără o bună optimizare a codului.

Optimizarea codului este o modificare a programelor efectuată de un compilator sau interpret de optimizare pentru a le îmbunătăți caracteristicile, cum ar fi performanța sau compactitatea, fără a modifica funcționalitatea.

Ultimele trei cuvinte din această definiție sunt foarte importante: indiferent cât de mult optimizarea îmbunătățește caracteristicile programului, trebuie să păstreze în mod necesar sensul original al programului în orice condiții. De aceea, de exemplu, există diferite niveluri de optimizare în GCC
Să mergem mai departe: există optimizări independent de mașină (nivel inalt)Și dependent de mașină (nivel scăzut). Semnificația clasificării este clară din denumiri, în optimizările dependente de mașină sunt utilizate caracteristici ale arhitecturilor specifice, în optimizările de nivel înalt, optimizarea are loc la nivelul structurii codului.

Optimizările pot fi, de asemenea, clasificate în funcție de domeniul de aplicare a acestora în local (operator, succesiune de operatori, bloc de bază), intra-procedural, inter-procedural, intra-modul și global (optimizarea întregului program, „optimizare în timpul asamblarii” , „Optimizarea timpului de conectare”).

Depanarea unui modul pentru a identifica erorile logice

Depanare PS este o activitate care vizează detectarea și corectarea erorilor din PS folosind procesele de executare a programelor acestuia. Testare PS este procesul de executare a programelor sale pe un anumit set de date, pentru care rezultatul aplicației este cunoscut în prealabil sau sunt cunoscute regulile de comportare a acestor programe. Setul de date specificat este apelat Test sau pur și simplu Test. Astfel, depanarea poate fi reprezentată ca o repetare repetată a trei procese: testare, în urma căreia se poate constata prezența unei erori în PS, căutarea locului unei erori în programele și documentația PS și editarea programelor și a documentației pentru a elimina eroarea detectată. Cu alte cuvinte:

Depanare = Testare + Găsire erori + Editare.

În literatura străină, depanarea este adesea înțeleasă doar ca un proces de găsire și corectare a erorilor (fără testare), a căror prezență este stabilită în timpul testării. Uneori, testarea și depanarea sunt considerate sinonime. În țara noastră, conceptul de depanare include de obicei testarea, așa că vom urma tradiția stabilită. Cu toate acestea, luarea în considerare în comun a acestor procese în această prelegere face ca această discrepanță să nu fie atât de semnificativă. Cu toate acestea, trebuie remarcat faptul că testarea este utilizată și ca parte a procesului de certificare PS.



Principii și tipuri de depanare software

Succesul depanării PS este în mare măsură predeterminat de organizarea rațională a testării. La depanarea PS, în principal sunt găsite și eliminate acele erori, a căror prezență în PS este stabilită în timpul testării. După cum sa menționat deja, testarea nu poate dovedi corectitudinea PS; în cel mai bun caz, poate demonstra prezența unei erori în acesta. Cu alte cuvinte, nu se poate garanta că prin testarea software-ului cu un set de teste practic fezabil, este posibil să se stabilească prezența fiecărei erori prezente în software. Prin urmare, apar două probleme. Prima sarcină este să pregătiți un astfel de set de teste și să le aplicați PS pentru a detecta cât mai multe erori posibil în el. Cu toate acestea, cu cât procesul de testare (și depanarea în general) continuă, cu atât costul software-ului devine mai mare. De aici a doua sarcină: de a determina momentul în care este finalizată depanarea PS-ului (sau a componentelor sale individuale). Un semn al posibilității de încheiere a depanării este completitatea acoperirii de către testele trecute prin PS (adică testele la care se aplică PS) a multor situații diferite care apar în timpul execuției programelor PS și manifestare relativ rară a erorilor în PS la ultimul segment al procesului de testare. Acesta din urmă este determinat în conformitate cu gradul de fiabilitate cerut al PS, specificat în specificația de calitate a acestuia.

Pentru a optimiza suita de teste, de ex. pentru a pregăti un astfel de set de teste care să permită, pentru un anumit număr de ele (sau pentru un anumit interval de timp alocat pentru testare), să detecteze un număr mai mare de erori în PS, este necesar, în primul rând, să se planifice acest set în prealabil și, în al doilea rând, să folosească o strategie rațională de planificare (proiectare) teste. Proiectarea testului poate începe imediat după finalizarea etapei de descriere externă a PS. Există diferite abordări pentru dezvoltarea unei strategii de proiectare a testelor, care pot fi plasate în mod condiționat grafic între următoarele două abordări extreme. Abordarea extremă din stânga este că testele sunt concepute numai pe baza studierii specificațiilor PS (descrierea externă, descrierea arhitecturii și specificația modulului). Structura modulelor nu este luată în considerare în niciun fel, adică. sunt tratate ca cutii negre. De fapt, această abordare necesită o enumerare completă a tuturor seturilor de date de intrare, deoarece, în caz contrar, unele secțiuni ale programelor PS pot să nu funcționeze dacă se omite vreun test, ceea ce înseamnă că erorile conținute în acestea nu vor apărea. Cu toate acestea, testarea PS cu un set complet de seturi de date de intrare este practic imposibilă. Abordarea extremă corectă este aceea că testele sunt concepute pe baza studiului textelor programelor pentru a testa toate modurile în care este executat fiecare program PS. Dacă luăm în considerare prezența ciclurilor cu un număr variabil de repetări în programe, atunci poate exista și un număr extrem de mare de moduri diferite de executare a programelor PS, astfel încât testarea acestora va fi, de asemenea, practic imposibilă.

Testarea unitară

Fiecare sistem software complex constă din părți separate - module care îndeplinesc o anumită funcție ca parte a sistemului. Pentru a verifica funcționarea corectă a sistemului în ansamblu, trebuie mai întâi să testați fiecare modul al sistemului separat. În cazul unor probleme, acest lucru va facilita identificarea modulelor care au cauzat problema și eliminarea defectelor corespunzătoare ale acestora. Această testare a modulelor în mod individual se numește test unitar ( testarea unitară).

Pentru fiecare modul testat, este dezvoltat un mediu de testare care include un driver și stub-uri, cerințele de testare și planurile de testare sunt pregătite care descriu cazuri de testare specifice.

Scopul principal al testării unitare este de a verifica dacă fiecare modul individual al sistemului îndeplinește cerințele înainte de a fi integrat în sistem.

În același timp, în timpul testării unitare sunt rezolvate patru sarcini principale.

1. Găsirea și documentarea neconformităților- Aceasta este o sarcină clasică de testare, care include nu numai dezvoltarea unui mediu de testare și a cazurilor de testare, ci și execuția testelor, înregistrarea rezultatelor execuției și raportarea problemelor.

2. Sprijin pentru dezvoltarea și refactorizarea arhitecturii de sistem de nivel scăzut și a comunicațiilor inter-module- această sarcină este mai caracteristică metodologiilor „uşoare” precum XP, unde se aplică principiul testării înainte de dezvoltare (Test-driven development), în care sursa principală de cerinţe pentru un modul software este un test scris înaintea modulului în sine. Cu toate acestea, chiar și cu schema clasică de testare, testele unitare pot dezvălui probleme în proiectarea sistemului și mecanisme ilogice sau confuze pentru lucrul cu un modul.

3. Suport pentru refactorizarea modulelor- Această sarcină este legată de susținerea procesului de schimbare a sistemului. Destul de des, în timpul dezvoltării, este necesară refactorizarea modulelor sau a grupurilor acestora - optimizarea sau reproiectarea completă a codului programului pentru a crește mentenabilitatea, viteza sau fiabilitatea acestuia. Testele unitare, totuși, sunt un instrument puternic pentru a verifica dacă noua versiune a codului programului funcționează exact la fel ca cea veche.

4. Suportă depanare și depanare- Această sarcină este asociată cu feedback-ul pe care dezvoltatorii îl primesc de la testeri sub formă de rapoarte de probleme. Rapoartele detaliate ale problemelor compilate în timpul fazei de testare unitară permit localizarea și eliminarea multor defecte dintr-un sistem software în primele etape ale dezvoltării acestuia sau dezvoltării noii sale funcționalități.

Datorită faptului că modulele testate sunt de obicei mici, testarea unitară este considerată cea mai simplă etapă (deși destul de consumatoare de timp) a testării sistemului. Cu toate acestea, în ciuda aparentei sale simplități, există două probleme cu testarea unitară.

1. Nu există principii uniforme pentru a determina ce este exact un modul separat.

2. Diferențele de interpretare a conceptului însuși de testare unitară - dacă înseamnă testarea separată a unui modul, a cărui funcționare este susținută numai de mediul de testare, sau dacă este vorba de verificarea corectitudinii modulului ca parte a unui sistem deja dezvoltat. Recent, termenul de „testare unitară” este folosit mai des în al doilea sens, deși în acest caz vorbim mai mult despre testarea integrării.



Se încarcă...
Top