Intro

Toliau patikrinsime keletos faktų/mitų teisingumą. Žinoma, jų buvo galima prirašyti daug daugiau, bet aš paminėjau tik kelis…

Vertindamas vieno ar kito kodo gabaliuko greitį, jį idedu į ciklą, paleidžiu ir žiūriu kiek laiko jis sukasi.

Mitas: length(str)=0 vs. str=”

Ar:
if length(str) = 0
yra greičiau už:
if str = '' then

kodėl taip turėtų būti:
nes antru atveju reikia sukurti tuščią string tipo konstantą ir sulyginti (inicijuoti ciklą, nors jis ir suksis 0 kartų). O pirmu atveju reikia tik pažiūrėti į pirmąjį string’o baitą – jame įrašytas ilgis (jei ten 0 vadinasi str=”)

Ar tai tiesa:
NE, pirmoji sąlyga tikrinama maždaug 4 kartus ilgiau!

Kodėl:
pirmoji sąlyga naudoja funkciją. Funkcijos kvietimas ilgas procesas – išsaugomas PC (Program Counter) registras, keičiamas BP registras, sukišami parametrai… gale viskas atvirkščia tvarka.

Mitas: i++ vs. i=i+1

Ar:
Inc(i); //
i++; // C++ Builder

yra greičiau už:
i := i +1; //
i = i +1; // C++ Builder

kodėl taip turėtų būti:
nes pirmu atveju yra skirta atskira instrukcija, kuri įvykdoma per vieną procesoriaus taktą.

Ar tai tiesa:
NE, vykdoma tiek pat laiko.

Kodėl:
Turbut kompiliatorius pagal nutylėjimą optimizuoja tokį kodą – aptikęs pakeičia į vieną instrukciją.

Calling conventions ir greitis

:
Directive Parameter order Clean-up Passes params in registers?
register Left-to-right Routine Yes
pascal Left-to-right Routine No
cdecl Right-to-left Caller No
stdcall Right-to-left Routine No
safecall Right-to-left Routine No

10^9 kartų kviečiama funkcija naudojant “register” kvietimo būdą, vykdoma:
7 sek 156 msek

10^9 kartų kviečiama funkcija naudojant “pascal”/”stdcall” kvietimo būdą, vykdoma:
10 sek 516 msek

Kodėl:
“register” būdu kviečiant net trys pirmieji parametrai paduodami per procesoriaus registrus (EAX, EBX, ECX) o ne kišami į steką, kas sutaupo kreipčių į RAM. Kiti kvietimo būdai visgi reikalingi – rašant dll bibliotekas (kitomis kalbomis) ir kitais atvejais.
Pagal nutylėjimą naudoja “register” būdą - Jums nieko nereikia daryti/optimizuoti :)

Operatorius “WITH”

With Listbox1.Items, Table1, Form1 do
while not EOF do
begin
Insert(0, FieldByName('COUNTRY').AsString);
Next;
end;

Nors kodas susikompiliuoja, bet jis veikia ne taip kaip norėjo programuotojas.
Pirma, operatorių “with” delphis interpretuoja ne iš kairės į dešinę, o iš dešinės į kairę. Todėl, jeigu Form1 turėtų metodą/savybę EOF tai iškviestų būtent Form1.EOF. Jei Form1 neturėtų (žinome, kad taip ir yra), tai bandytų Table1.EOF, ir jei tai “nepavyktų” tada tik ListBox1.Items.EOF. Antra, šio kodo tikrai nepavadinsi lengvai skaitomu - gerai, kad šiame pavyzdyje panaudoti mums gerai žinomi objektai/metodai, o kas jei čia būtų programuotojo sukurti objektai, kurių struktūros mes nežinome?

Taisome situaciją… Iškeliame Table1. Sukompiluojame - viskas gerai:

with Listbox1.Items, Form1 do
while not EOF do
begin
Insert(0, Table1.FieldByName('COUNTRY').AsString);
Table1.Next;
end;

Ar ištaisėme situaciją? Ne. Palikome “EOF”. Dabar EOF vykdomas ne iš Table1 objekto, o kaip atskira nepriklausoma funkcija, kuri aprašyta “System.pas” faile.

Kaip pamatėme, panaudojus operatorių “WITH” kodo skaitomumas suprastėja, atsiranda klaidų, kurios neįgudusiai akiai nepastebimos.

Reiškinių skaičiavimas

Pradinės reikšmės:
int a = 0;
int b = 2;

Reiškinys:

a = b*2 + ++b*3;

ir:

a = ++b*2 + b*3;

Duoda tą patį rezultatą 15 (ant Borland C++ Builder), nes pirma padidinamos reikšmės (šiuo atveju ++b) o tik tada suskaičiuojamas reiškinys. Nors yra tokių kompiliatorių, kurie veiksmus atlieka nuosekliai… deja ir tuo viskas nesibaigia - vieni vykdo iš dešinės į kairę (atsakymai būtų atitinkamai 15 ir 12) kiti iš kairės į dešinę.

Tipų tikrinimas

Parašius:

for (int i=0; i++; i<5) ...

Borland C++ Builder 6 kompiliatorius neišmeta nei užuominos (Hint) nei įspėjimo (Warning) :) Jūs irgi nemestumėt, nes nematot priežasties, dėl ko reiketų? :) Ciklo tęsimo/nutraukimo sąlyga ir iteratoriaus didinimas yra sukeisti vietomis…

global/public/private metodai

(Bandyta su Borland Delphi7)
Tiek globali procedūra, tiek public metodas iškviečiamas per vienodą laiką. Private metodo (žinoma, kviečiamas iš objekto vidaus) iškvietimas trunka ~10% trumpiau. Deja, kad gautumėt daugiau ir tikslesnės informacijos turėsit pasinagrinėti patys…

Panašūs straipsniai


“Kompiliatoriai, kodo optimizavimas, mitai” komentarų: 7

  1. ernetas

    Mano nuomone reikėtų daugiau tokių tip’ų.

  2. dado1945

    Kratinys kažkoks :) Jei rašai apie optimizacijas tai ir rašyk tik apie jas. Ir tai tokios neįdomios paimtos. Ale konstantinės. Kam tai aktualu šiais laikais?

  3. menesis

    “nes antru atveju reikia sukurti tuščią string tipo konstantą ir sulyginti (inicijuoti ciklą, nors jis ir suksis 0 kartų). O pirmu atveju reikia tik pažiūrėti į pirmąjį string’o baitą – jame įrašytas ilgis (jei ten 0 vadinasi str=’’)”

    tiek pirmas, tiek antras paaiškinimas nėra teisingi. 1. konstantų sukurti nereikia, jos gyvena duomenų segmente. == yra tik overloadintas operatorius, taigi galų gale irgi funkcija. 2. dar yra programavimo kalbų kurios saugo stringo ilgį pirmam baite? paprastai stringo galą nurodo NUL; kartais stringas turi length property, kartais len() funkcija turi perbėgti visą stringą, kad grąžintų ilgį.

    —–

    for (int i=0; i++; i

  4. Raigedas

    menesis:
    ok, kaip sakiau, tai tik mitai, ir aš netikrinau visų smulkmenų… tiesiog kažkur girdėjau, kad shortstring’o pirmas fizinis baitas rodo ilgį ir tiek. O kur Tu kalbi apie teksto pasibaigimą #0 simbolių tai čia PAnsiChar tipas. string’as implementuojamas (bent pas borlandą) sudėtingiau nei PAnsiChar.

  5. Raigedas

    dado1945:
    taip, tai tikrai kratinys. tiesiog pagalvojau kad ir jis gali būti kam nors naudingas…

  6. Gudis

    visai idomu buvo paskaityt :)

  7. Adamsas

    Ar zinai kaip veikia optimizavimas? Jei nori ziureti kiek su optimizavo ziurek i asm koda o ne i laika. Kitas jei nori lyginti optimizuota koda su ne optimizuotu tai isjunk optimizavimo funkcije kompiliatoriuje.O ne rasyk koda kuris turi veikti ilgiau, bet optimizacijos deka veikia vienodai. Ir isvadu nedaryk neteisingu:)

Rašyti komentarą

Jūs privalote prisijungti jeigu norite rašyti komentarą.