Autor Téma: Převod recordu ze ShortString na String - uvolnovaní paměti a jiné ..  (Přečteno 623 krát)

Offline Jirka

  • Hrdina
  • ****
  • Příspěvků: 333
  • Karma: 9
    • Verze Delphi: XE2

V současne době pouzivám pro ukladaní záznamů do Tlist  pomocí New() pouze recordy které mají u stringu pevné délky

Má otázka zní jaké potencialní problémy mě mohou čekat, pokud bych přešel  na referenční stringy ?
Například při uvolnění paměti  pomocí  Dispose()  nebo jiné manipulaci s recordem

Kod používám bez problému , důvodem je odstranění warning hlášky
 W1058 Implicit string cast with potential data loss from 'string' to 'ShortString'


Kód: Delphi [Vybrat]
  1.  PMujZaznam = ^rMujZaznam
  2.  
  3.  rMujZaznam = packed record
  4.     Id: integer;
  5.    Jmeno:String[20];
  6.    Prijmeni:String[20];

převést na
 
Kód: Delphi [Vybrat]
  1. rMujZaznam = packed record
  2.     Id: integer;
  3.    Jmeno:String;
  4.    Prijmeni:String;


Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
Má otázka zní jaké potencialní problémy mě mohou čekat, pokud bych přešel  na referenční stringy ?
Například při uvolnění paměti  pomocí  Dispose()  nebo jiné manipulaci s recordem
Rekl bych ze v prvni rade tohle: http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/System_Finalize@.html

A ve druhe rade si rozdrobis heap, protoze hodnota ShortString je vnitrne take record a lezi uvnitr recordu, takze alokujes pamet jednou pro vsechno, zatimco jakmile pouzijes nejaky huge string, tak record obsahuje jen pointer a vlastni obsah stringu je alokovan na heapu, coz obnasi vetsi overhead, coz by nekdy mohlo vadit.

Ve treti (nebo mozna v prvni) rade z toho plyne dalsi problem, ze jednoduse nemuzes record jako zmolek pameti poslat/prijmout v komunikaci nebo zapsat/precist z disku, protoze tam budes mit jen pointery a ne obsahy a budes se nejak muset vyporadat se serializaci/deserializaci obsahu recordu do/z streamu.

Osobne bych to nedelal a tam kde mas warningy uvedl explicitni typecast na znameni, ze o tom vis.





Offline Jirka

  • Hrdina
  • ****
  • Příspěvků: 333
  • Karma: 9
    • Verze Delphi: XE2
Osobne bych to nedelal a tam kde mas warningy uvedl explicitni typecast na znameni, ze o tom vis.

tuto radu velmi rád poslechnu ..

jen bych tomu měl dva doplňkové dotazy:

1. týká se nějaký výše uvedený problém v této konstrukci ? (samozřejmě mimo větší režie)

Kód: Delphi [Vybrat]
  1. procedure  VstupniProcedura;
  2.  Var MujZaznamWork :  rMujZaznam;
  3.  begin  
  4.  MujZaznamWork.Jmeno:='František';
  5.  MujZaznamWork.Primeni:='Lebeda';
  6.  ZpracovatUdaje(MujZaznamWork);  // vkládaný record může být "var" "const" nebo "kopie"
  7. end;
  8.  

2.  Může se alokovany record pomocí New() a následně   špatně  uvolněný record   stát příčinou  vyvolání výjimky  při ukončení aplikace (v oblasti System.FinalizeUnits) ?



Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
1. týká se nějaký výše uvedený problém v této konstrukci ? (samozřejmě mimo větší režie)
Kód: Delphi [Vybrat]
  1. procedure  VstupniProcedura;
  2.  Var MujZaznamWork :  rMujZaznam;
  3.  begin  
  4.  
Krome bodu 1) ano. Ale kdyz se jedna o record alokovany prekladacem, tak ten vygeneruje volani InitializeRecord a pri opousteni subrutiny FinalizeRecord

Citace
2.  Může se alokovany record pomocí New() a následně   špatně  uvolněný record   stát příčinou  vyvolání výjimky  při ukončení aplikace (v oblasti System.FinalizeUnits) ?
Muze. Co se presne stane zalezi na obsahu pameti po New(), to muze spadnout uz pri pokusu prirazeni stringu, protoze to zkousi intepretovat v podstate nahodily obsah pameti jako string, ktery ma na zapornych offsetech reference counter a skutecnou delku retezce, se kterou se to bude pokouset pracovat... Proto je treba zavolat to InitializeRecord, ktere ty skryte hodnoty huge stringu vynuluje.



Offline Jirka

  • Hrdina
  • ****
  • Příspěvků: 333
  • Karma: 9
    • Verze Delphi: XE2

Citace
2.  Může se alokovany record pomocí New() a následně   špatně  uvolněný record   stát příčinou  vyvolání výjimky  při ukončení aplikace (v oblasti System.FinalizeUnits) ?
Muze. Co se presne stane zalezi na obsahu pameti po New(), to muze spadnout uz pri pokusu prirazeni stringu, protoze to zkousi intepretovat v podstate nahodily obsah pameti jako string, ktery ma na zapornych offsetech reference counter a skutecnou delku retezce, se kterou se to bude pokouset pracovat... Proto je treba zavolat to InitializeRecord, ktere ty skryte hodnoty huge stringu vynuluje.

Lze za  stejně závadnou konstrukci považovat i toto ?

Kód: Delphi [Vybrat]
  1.     rMujZaznam = packed record
  2.         Id: integer;
  3.        Jmeno:String;
  4.        Prijmeni:String;
  5.  
  6.  DatovePole array of rMujZaznam ;
  7.  
  8. // přidání recordu do pole
  9. procedure Objekt.Append(const Value: rMujZaznam );
  10. begin
  11.   SetLength(DatovePole , length(DatovePole ) + 1);
  12.   DatovePole [high(DatovePole )] := Value;
  13. end;
  14.  
  15.  
  16. //  kompletní dealokace recordů i vlastního pole
  17. procedure Objekt.Clear;
  18. begin
  19.  SetLength(DatovePole ,0)
  20. end;
  21.  
  22.  

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
Lze za  stejně závadnou konstrukci považovat i toto ?
Kód: Delphi [Vybrat]
  1.   SetLength(DatovePole , length(DatovePole ) + 1);
  2.   ...
  3.   SetLength(DatovePole ,0)
  4.  

Ne, protoze nove polozky u dynamickeho pole jsou po alokaci vyplneny (FillChar) nulami podobne jako member variables a pri dealokaci pole se vola DynArrayClear, ktere proleze pole polozku po polozce a podle RTTI pripadne zavola potrebnou finalizaci.

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 1109
  • Karma: 47
    • Verze Delphi: 10.3
Lze za  stejně závadnou konstrukci považovat i toto ?
Kód: Delphi [Vybrat]
  1. SetLength(DatovePole , length(DatovePole ) + 1);

Ale pokud je těch záznamů hodně, bude takovéhle přidávání po jednom strašně neefektivní.

Offline Jirka

  • Hrdina
  • ****
  • Příspěvků: 333
  • Karma: 9
    • Verze Delphi: XE2
Ale pokud je těch záznamů hodně, bude takovéhle přidávání po jednom strašně neefektivní.

Nějak se to neměřil ale tato neefektivita je pro mě asi víceméně bezpředmětná protože se jedná  běžně o 10 - 200   položek
Teoreticky bych mohl  zjistit počet zaznamů přes SELECT Count(*) FROM  , ale zatím jsem to neměl potřebu řešit.
Pokud si pamatuji, tu efektivitu tady jedno téma  nedávno probíralo

Offline František

  • Guru
  • *****
  • Příspěvků: 596
  • Karma: 6
    • Verze Delphi: primárne v XE5, občas 10.2.3 comunity
nie je mi jasné, akú má v súčasnosti výhodu použitie dynamického poľa pred Tlist

Offline Jirka

  • Hrdina
  • ****
  • Příspěvků: 333
  • Karma: 9
    • Verze Delphi: XE2
nie je mi jasné, akú má v súčasnosti výhodu použitie dynamického poľa pred Tlist
V mém případě asi jednodušší implementace  ;)

Offline KarelHorky

  • Plnoletý
  • ***
  • Příspěvků: 219
  • Karma: 9
    • Verze Delphi: XE6, Delphi 10.2 Tokyo
Ale pokud je těch záznamů hodně, bude takovéhle přidávání po jednom strašně neefektivní.

Nějak se to neměřil ale tato neefektivita je pro mě asi víceméně bezpředmětná protože se jedná  běžně o 10 - 200   položek
Teoreticky bych mohl  zjistit počet zaznamů přes SELECT Count(*) FROM  , ale zatím jsem to neměl potřebu řešit.
Pokud si pamatuji, tu efektivitu tady jedno téma  nedávno probíralo

Při každém prodloužení pole se vytvoří v paměti nová kopie, vždy s jedním přidaným záznamem. A pořád dokola. Pokud bys pracoval s několik tisíc řádků dlouhým polem, můžeš vyčerpat paměť a dostat Out of memory. A je to opravdu velmi pomalé. Při stejné velikosti pole můžou být rozdíly i několik minut, na dnešních PC. Ověřeno vlastní zkušeností.
Win10 Prof 64b, Firebird 2.5

Offline Jirka

  • Hrdina
  • ****
  • Příspěvků: 333
  • Karma: 9
    • Verze Delphi: XE2
Při každém prodloužení pole se vytvoří v paměti nová kopie, vždy s jedním přidaným záznamem. A pořád dokola.
A neměla by se ta stará kopie automaticky uvolňovat ?

A je to opravdu velmi pomalé. Při stejné velikosti pole můžou být rozdíly i několik minut, na dnešních PC. Ověřeno vlastní zkušeností.
Až budu mít trochu chvilku,  tak si to určitě taky zkusím

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
A neměla by se ta stará kopie automaticky uvolňovat ?
To ona se uvolni, ale pak muzes mit celou pamet rozfrcanou po volnych kouskach o delce n-1 az n-n+1 elementu. Memory manager na bazi FastMM, ktery byl pred lety v Delphi implementovan, by na to tak citlivy nemel byt - vse zavisi na tom, jaky profil promennych potrebujes alokovat a jaky uvolnujes.

Offline KarelHorky

  • Plnoletý
  • ***
  • Příspěvků: 219
  • Karma: 9
    • Verze Delphi: XE6, Delphi 10.2 Tokyo
Při každém prodloužení pole se vytvoří v paměti nová kopie, vždy s jedním přidaným záznamem. A pořád dokola.
A neměla by se ta stará kopie automaticky uvolňovat ?
No měla, ale kdy to memory manager udělá ... S tím Out of memory jsem se několikrát setkal a vždy pomohlo předělat to na alokaci celého pole najednou. I když se dělají dva selecty, jednou select count() where, pak select sloupce where, tak výsledek je mnohem rychlejší. Ale už se musí jednat o řádově desítky tisíc záznamů.
Win10 Prof 64b, Firebird 2.5

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
Excellent
Rated 1 time
No měla, ale kdy to memory manager udělá ... S tím Out of memory jsem se několikrát setkal a vždy pomohlo předělat to na alokaci celého pole najednou.
MM ty bloky uvolni hned, ale k tomu Out of memory dochazelo v minulosti se starym borlandskym MM, ktery drobil pamet, zejmena pri pouzitim nevhodnych technik jako je tato. Duvodem pro preteceni pameti bylo, ze po n realokacich bylo dostupnych volnych bloku pameti o velikosti 1..n-1*sizeof(element), ale v urcitem stavu uz jsi nemel k dispozici pamet pro blok o velilkosti n+1*sizeof(element). Nahrazenim pyvodniho MM za FastMM, ktery kategorizuje pametove bloky podle velikosti, se to vyrazne zlepsilo.