Avoiding implicit try finally section/fi
│
English (en) │
suomi (fi) │
Bahasa Indonesia (id) │
русский (ru) │
Epäsuoran try finally lohkon välttäminen
Yleistä
Koodin optimoinnissa se auttaa ymmärtämään, että kääntäjä käärii tiettyjä koodirakenteita epäsuorasti
try
…
finally
-lohkoon.
Tämä on tarpeen aina, kun käytetään sellaisia muuttujia, kuten ansiString
,
variant
s tai dynaamisia taulukoita jotka edellyttävät initialization ja finalization (esim. kun standardi aliohjelmat initialize
ja finalize
tarvitaan oikean muistin allokoinnin ja hankitun muistin vapauttamiseksi).
Tällöin kääntäjä varmistaa, että msg: n viitenumero lasketaan asianmukaisesti, kun menettely doSomething poistuu poikkeuksella. Usein tämä voi kuitenkin aiheuttaa merkittäviä haitallisia vaikutuksia tuotetun koodin nopeuteen.
Tämä oli ongelma, joka oli fpc-devel -sähköpostilistan aiheketjussa http://www.mail-archive.com/fpc-devel@lists.freepascal.org/msg01367.html TList
slowness classes .
Huomaa, että väliaikaiset ansiString
-muuttujat voidaan luoda epäsuorasti. Ainoa tapa olla täysin varma siitä, mitä todellisuudessa tehdään, on lukea assemblerin ulostulo.
Mahdolliset ratkaisut
- Käytetään
{$implicitexceptions off}
: Varmistetaan, että tämä koskee vain julkaistuja versioita. Virheenjäljitys voi muuttua hankalaksi, kun siirrytään erityisesti muistivuotoihin ja korruptioon. - Irrotetaan harvoin käytetty koodi, joka aiheuttaa implisiittisen
try…finally
eri aliohjelmiin. (Voidaan käyttää aliohjelmissa aliohjelmia) - Käytetään
const
parametreja arvoparametrien sijaan. Näin vältetään tarve muuttaa viittausosoitusta, mutta tilapäiset muuttujat voivat edelleen olla ongelma. - Käytetään globaaleja muuttujia: Sinun on kuitenkin oltava varovainen uusintakysymyksissä, mutta väliaikaiset muuttujat voivat silti olla ongelma.
- Käytetään ei-viitteellisiä tyyppejä, kuten
shortstring
.
riskejä ja milloin niitä sovelletaan
Warning: Nämä poikkeuskehykset luodaan syystä. Jos niistä jätetään poikkeus niin koodiin jään muistivuoto mahdollisuus
Vuonna 2007 {$implicitExceptions}
lisättiin
strutils
-käännösyksikköön.
Jota sysutils on luultavasti seurannut. Tätä varten noudatettiin seuraavaa lähestymistapaa:
- Rutiini, joka kutsuu rutiinia, joka nostaa poikkeuksia, on vaarallinen - esim.
strToInt
, mutta eistrToIntDef
. - Poikkeuksia aiheuttavat rutiinit on huomioitavia.
- Hyvin suuret rutiinit eivät ole vaivan arvoista, koska riski ja alhaiset edut - esim. päivämäärän muotoilurutiinit.
- Desimaalilukujen käyttö voi nostaa poikkeuksia, jotka muunnetaan
sysUtils
-ohjelmien avulla. En ole varma, onko tämä todellakin riittävä syy, mutta ohitin Desimaalilukujen käytön käyttämällä näitä rutiineja aluksi tästä syystä.
Jos havaitset näitä muutoksia, ota yhteyttä Marcoviin.
Demo-ohjelma
Alla on pieni demo-ohjelma
Sen suorittaessaan osoittaa selvästi, että epäsuoran try … finally
välttäminen mhdollistaa koodin suorittamisen paljon nopeammin. Kun suoritin tämän ohjelman järjestelmässäni, sain tuloksen:
time of fooNormal: 141 time of fooFaster: 17
- Tässä näytetään temppu, jolla vältetään epäsuoria
try … finally
- lohkoja (muuttamatta koodin merkitystä tai turvallisuutta) joissakin tapauksissa (kun sinun ei tarvitse tosiasiallisesti käyttää tätäAnsiString
/
Variant
-tyyppiä/ jotakin muuta tyyppiä joka kerta kun aliohjelmaa kutsutaan mutta vain jos jollakin parametrilla on tietty arvo).
program implicitExceptionDemo(input, output, stderr);
// for exceptions
{$mode objfpc}
// data type 'string' refers to 'ansistring'
{$longstrings on}
uses
{$IFDEF UNIX}
// baseUnix, unix needed only to implement clock
BaseUnix, Unix,
{$ENDIF}
sysUtils;
function clock(): int64;
var
{$IFDEF UNIX}
dummy: tms;
{$ELSE}
TS : TTimeStamp;
{$ENDIF}
begin
{$IFDEF UNIX}
clock := fpTimes(dummy);
{$ELSE}
TS:=DateTimeToTimeStamp(Now);
result := TS.Time;
{$ENDIF}
end;
// Note: when fooNormal and fooFaster are called
// i is always >= 0, so no exception is ever actually raised.
// So string constants SNormal and SResString are not really used.
procedure fooNormal(i: integer);
var
s: string;
begin
if i = -1 then
begin
s := 'Some operation with AnsiString';
raise exception.create(s);
end;
end;
procedure fooFaster(i: integer);
procedure raiseError;
var
s: string;
begin
s := 'Some operation with AnsiString';
raise exception.create(s);
end;
begin
if i = -1 then
begin
raiseError;
end;
end;
// M A I N =================================================
const
testCount = 10000000;
var
i: integer;
start: int64;
begin
start := clock();
for i := 0 to testCount do
begin
fooNormal(i);
end;
writeLn('time of fooNormal: ', clock() - start);
start := clock();
for i := 0 to testCount do
begin
fooFaster(i);
end;
writeLn('time of fooFaster: ', clock() - start);
end.
Laittamalla raiseError
toiseen näkyvyyteen fooFaster
aliohjelmassa, keskeysten käsittely ei tule osaksi pääkäyttöistä suoritusosaa.