Případ FieldByName

vložil Radek Červinka 6. března 2018 00:36

Jak se tak potuluji po zákaznících se školením nebo konzultacemi, tak narážím na opakovanou situaci s FieldByName (případně podobnými funkcemi).

Odbočka: Nemám rád datamoduly plné různých SQL příkazů a datasetů (popravdě mne přímo děsí), jsem zastáncem dynamických SQL a datasetů vytvořených až je třeba. Důvodem je pro mne lepší modularita, lepší udržovatelnost, nahraditelnost a vůbec samá pozitiva a sociální jistoty.

Na druhou stranu pokud tedy už do datamodulu vložíte nějaký dataset, můžete definovat jeho fieldy a Delphi vytvoří objekty (resp. komponenty) reprezentující jednotlivé fieldy, jako např. TStringField, TIntegerField atd. Všechny jsou následníkem TField.

Myslím si, že původní návrh Delphi předpokládal, že programátoři budou v podstatě používat pouze tyto vizuální objekty a tak rychle přistupovat k datům, přes property daného objektu. Jenže z hlediska udržovatelnosti, čitelnosti atd. se hodně často místo toho používá dynamické nalezení TField v datasetu podle jména přes FieldByName.

var
  ds: TDataSet;
  x : Integer;
begin
// ds je naplněn daty
  x := 0;

  while not ds.eof do
  begin
    x := x + ds.FieldByName('ParamA').AsInteger + ds.FieldByName('ParamB').AsInteger;
    ds.Next;
  end;
end;

Věřím, že je to celkem klasická konstrukce a funguje uspokojivě, data se sečtou atd.

Co je špatně, resp. co se dá optimalizovat? Samozřejmě zrušit zbytečné akce, které se během cyklu nemění (invariantu cyklu). FieldByName(xx) vrátí pořád stejného následníka TField během všech iterací, proto je zbytečné ho pořád zjišťovat.

var
  ds: TDataSet;
  x : Integer;
  oFieldParamA, oFieldParamB: TField;
begin
// ds je naplněn daty
  x := 0;
  oFieldParamA := ds.FieldByName('ParamA');
  oFieldParamB := ds.FieldByName('ParamB');
  while not ds.eof do
  begin
    x := x + oFieldParamA.AsInteger + oFieldParamB.AsInteger;
    ds.Next;
  end;
end;

Program se nám trochu nepatrně prodloužil a vzhledem k zvýrazňování textu i krapánek možná více znepřehlednil, ale výsledek je velmi výrazně efektivnější.

Pozorný čtenář jasně chápe jak moc ušetříme, ale přesto: oFieldParamA.AsInteger je v podstatě jen odkaz na metodu objektu (v ideálním případě jen pár instrukcí), kdežto FieldByName znamená hledání podle jména v seznamu stringu (interně je volán FindField) a teprve pak to zavolání metody objektu. Konkrétně pro zajímavost např.

Delphi 5

function TFields.FindField(const FieldName: string): TField;
var
  I: Integer;
begin
  for I := 0 to FList.Count - 1 do
  begin
    Result := FList.Items[I];
    if AnsiCompareText(Result.FFieldName, FieldName) = 0 then Exit;
  end;
  Result := nil;
end;

Jednoduché prohledání…

Delphi XE

function TFields.FindField(const FieldName: string): TField;
var
  I: Integer;
  HashValue: Cardinal;
begin
  if FList.Count > 0 then
  begin
    HashValue := TNamedItem.HashName(FieldName);
    for I := 0 to FList.Count - 1 do
    begin
      Result := FList.Items[I];
      if (Result.FFieldNameHashValue = HashValue) and
         (AnsiCompareText(Result.FFieldName, FieldName) = 0) then
        Exit;
    end;
  end;
  Result := nil;
end;

Prohledání přes hash, ale všimněte si, že po nalezení hashe je provedeno ještě prověření.

Aktuální Delphi

function TFields.FindField(const FieldName: string): TField;
begin
  FDict.TryGetValue(AnsiLowerCase(FieldName), Result);
end;

kde FDict: TDictionary<string, TField>, ale pozor: AnsiLowerCase taky něco stojí.

Je vidět, že autoři Delphi jsou si vědomi toho, co se používá a snaží se i neoptimální kód optimalizovat přímo v RTL.

Naše "optimalizace" bude tím efektivnější, čím bude požadovaný field dále v seznamu fieldů, resp. čím bude seznam fieldů rozsáhlejší a hlavně čím bude větší počet řádků v datasetu, protože už při dvou záznamech v uvedeném případě urychlíme původní program cca o polovinu (to co by se provádělo 2x se provede jen zhruba 1x, všechno ostatní je proti FieldByName zanedbatelné - no možná až na Next).

Poznámky klidně napište do komentářů.

Tagy: , ,

Optimalizace | Praxe

Komentáře

6.3.2018 10:21:18 #

anec

100% potvrzuji. píšu ještě v D7 a tam je to úspora jako prase. čím více fieldbyname v iteraci, logicky tím větší úspora. přehlednost je menší, takže při prvnotním programování používám fieldbyname a když mi vše funguje jak má tak přepíšu na field.as ....

anec

6.3.2018 11:20:16 #

bohdan

Dřív jsem používal FieldByName, ale nyní už jen FindField. Rozdíl mezi FindField a FieldByName je, že FindField při neexistenci sloupce v datasetu vrací nil, ale FieldByName zahlásí vyjimku. Jinak souhlasím s Radkem, že před smyčkou je dobré si field vytáhnout a pak už pracovat jen s odkazem. Je to určitě rychlejsší.

Nezavrhuji datamodul a myslím, že to svůj význam má.
Používám Datamodul i dynamické SQL a podle mě záleží na tom co potřebuji s výsledkem dělat. Pokud dataset potřebuji napojit na vizuální komponenty, tak dataset dám do datamodulu, pokud potřebuji nevizuálně pracovat s daty dělám dynamicky. Dělám i dynamcké formuláře a tam taky dělám dynamické SQL.

bohdan

6.3.2018 13:05:47 #

radekc

>Pokud dataset potřebuji napojit na vizuální komponenty, tak dataset dám do datamodulu, pokud potřebuji nevizuálně pracovat s daty dělám dynamicky.

Pokud stejný dataset potřebuji v jiném modulu (exe), tak jsem skončil bez toho abych se hrabal v datamodulu, nebo tam vrazil celý datamodul.

Pokud potřebuji SQL změnit, např. aplikace práv, tak jsem ztratil poslední výhodu to mít v datamodulu.

Migroval jsem system z ADO na FireDAC, s tim, ze v celem programu se pouzival naslednik ADOQuery, tak jsem jen změnil předka a dal rekompilovat. Zadne problemy s chybejicimi property atd. Navíc definice fieldu v datamodulu je peklo, zvláště když se SQL mění.

Ale to byl jen můj názor.

radekc

7.3.2018 20:19:12 #

František

a ma to nejaký vplyv na konštrukciu Fields[i].AsInteger ?

František

7.3.2018 22:28:45 #

radekc

>a ma to nejaký vplyv na konštrukciu Fields[i].AsInteger
ne

radekc

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS

Dle měsíců