Mam taki problem. Chciałem stworzyć dość dużą tablicę dynamiczną 64(rozmiar rekordu)x5mln czyli 320MB. Ale przy alokacji 230MB wywala mi wyjątek EOutOfMemory. Może ktoś mi wytłumaczy jak działa alokacja pamięci dla tablic. Mam WIndows 7, 4GB RAM.
Po program ma dyrektywę {$M 16384,1848576000}. Nie wiem co zrobić, menadżer windows pokazuję pamięć dla aplikacji na 228MB. Może Windows 7 ma limity przydziały pamięci dla pojedynczej aplikacji, nie mam pomysłu jak sprawić by poprawnie zaalokować pamięć dla mojej tablicy.
Alokujesz za pomocą SetLength oczywiście?
może Windows 7 ma limity przydziały pamięci dla pojedynczej aplikacji,
O ile pamiętam, 2 lub 3 GB na aplikację max.
czyli 320MB
Wliczyłeś w to rozmiar typu?
Dla przykładu, jeżeli taka duża tablica będzie typu Integer
(w systemie 32-bitowym), będzie miała rozmiar:
6450000004/1024/1024=1220 MB(!!!)
Zapewniam Cię, niech cię Boża ręka chroni przed alokacją takiej wielkiej tablicy!
Cokolwiek chcesz zrobić, da się to wykonać bez zajmowania 1.2 GB pamięci...
Wyliczyłem dobrze. Zmierzyłem rozmiar rekordu (uwzględnia rozmiary typów) przez SizeOf, razem daje to 64 bajty. Tablica jest bardzo duża, bo piszę aplikację, która ma przyśpieszyć te same operacje, które wykonuję na bazie danych. Na bazie danych trwa to kilka dni, dlatego chciałem to wykonać w pamięci RAM. Dziwi mnie to, że mając 4GB RAM odejmując z 1,5RAM na Windows brakuje mi pamięci przy 230MB.

http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/compdirsmemoryallocsizes_xml.html
Na tej stronie piszą, że maksymalny rozmiar stosu to 2.147.483.647 a przez Options->Linking mogę zaalokować tylko 16.777.216 wie ktoś dlaczego?
Tablice i tak nie są tworzone przecież na stosie...



- Rejestracja:ponad 12 lat
- Ostatnio:7 miesięcy
- Postów:6610
- podziel na mniejsze tablice
- zamiast tablicy zrób na liście jedno/dwu kierunkowej
Jak pokazywałem wczoraj zaalokowanie ponad 1GB pamięci w Delphi 7 / XE na WinXP /W7 nie jest problemem
procedure TForm5.Button5Click(Sender: TObject);
type
TBuf = array[1..64] of byte;
var
Arr: array of TBuf;
begin
SetLength(arr, 5000000);
ShowMessage(Inttostr(Length(arr)));
end;
w szczycie program zajmuje w pamięci trochę ponad 320MB
co więcej nawet SetLength(arr, 15000000)
przechodzi bez problemu i program zajmuje 860MB
Xitami napisał(a):
a nie jest to czasem lokalna zmienna (na stosie)?
Pole w klasie.
Próbowałem też z pola zrobić zmienną globalną - ten sam efekt dokładnie.
Wygląda na to, jakby wskaźniki do rekordów nie mieściły się w rozmiarze stosu 8x3.600.000=288.000.000, bo max rozmiar stosu (w Options-> Linking)to 16MB (dlaczego? a nie 2GB jak piszą na stronie Embarcadero).

- Rejestracja:ponad 12 lat
- Ostatnio:7 miesięcy
- Postów:6610
może zamiast opowiadać tak pięknie pokażesz wreszcie kod
Alokuję stopniowo co 10.000 rekordów.
A jak sądzisz co zrobi subsystem? Zaalokuje nową pamięć o ileśtam bajtów większą i wszystko przeniesie.
Tablice dynamiczne nie są wysokopoziomowe, one są stosunkowo głupie. Użyj czegoś innego, np. w FPC TFPGList<integer>
Wyliczyłem dobrze. Zmierzyłem rozmiar rekordu (uwzględnia rozmiary typów) przez SizeOf, razem daje to 64 bajty.
Jakie razem, może jest coś alignowane według ABI?
Skoro wyliczyłeś dobrze to znaczy że nie masz pamięci.
może zamiast opowiadać tak pięknie pokażesz wreszcie kod
Kod powinien być na początku. eh.
Tablice i tak nie są tworzone przecież na stosie...
Toż to oczywiste, jak ktoś myśli że na stosie to może od razu wyjść... Chodzi ofc o tablice dynamiczne.
Kod jest prostacki: deklaracja rekordu, deklaracja tablicy dynamicznej i SetLength() co 10.000 rekordów, obsługa wyjątku EOutOfMemory.
Znasz takie słowo jak KOD ???
Nie o błąd w kodzie mi chodzi, bo kod działa prawidłowo. Ale dla tych co chcą kodu proszę:
type TTransactionsRec=record
Id:LongInt;
Direction:Byte;
Currency:Byte;
Open:Double;
DataOpen:TDateTime;
Close:Double;
DataClose:TDateTime;
Zysk:Double;
Minimum:Double;
Maximum:Double;
end;
type TTransactions=array of TTransactionsRec;
var Transactions:TTransactions;
procedure TMain.FormCreate(Sender: TObject);
begin
MaxTransactions:=10000;
AddTransactions:=10000;
CountTransactions:=0;
Initialize(Transactions);
SetLength(Transactions,MaxTransactions);
end;
function TMain.CzytajHistorie(FileName:string):Boolean;
var StreamReader:TStreamReader;
Encoding:TEncoding;
Output : TSplit;
FormatSettings:TFormatSettings;
begin
Result:=true;
FormatSettings:=TFormatSettings.Create;
FormatSettings.TimeSeparator:=':';
FormatSettings.DateSeparator:='-';
FormatSettings.ShortDateFormat:='yyyy-mm-dd hh:nn:ss';
FormatSettings.DecimalSeparator:='.';
if FileExists(FileName) then
begin
StreamReader:=TStreamReader.Create(FileName,Encoding.ASCII);
repeat
Split(';',StreamReader.ReadLine,Output);
if (CountTransactions>=MaxTransactions) then
begin
MaxTransactions:=MaxTransactions+AddTransactions;
try
SetLength(Transactions,MaxTransactions);
except
on EOutOfMemory do
begin
Memo1.Lines.Add('Brak pamieci. Zaladowano: '+IntToStr(CountTransactions)+' rekordow.');
Result:=false;
Break;
end;
end;
end;
Transactions[CountTransactions].Id:=StrToInt(OutPut[0]);
if OutPut[1]='buy' then Transactions[CountTransactions].Direction:=1
else Transactions[CountTransactions].Direction:=2;
Transactions[CountTransactions].Currency:=StrToCurrency(OutPut[2]);
Transactions[CountTransactions].Open:=StrToFloat(OutPut[4],FormatSettings);
Transactions[CountTransactions].DataOpen:=StrToDateTime(OutPut[5],FormatSettings);
Transactions[CountTransactions].Close:=StrToFloat(OutPut[6],FormatSettings);
Transactions[CountTransactions].DataClose:=StrToDateTime(OutPut[7],FormatSettings);
Transactions[CountTransactions].Zysk:=StrToFloat(OutPut[8],FormatSettings);
Transactions[CountTransactions].Maximum:=StrToFloat(OutPut[9],FormatSettings);
Transactions[CountTransactions].Minimum:=StrToFloat(OutPut[10],FormatSettings);
Inc(CountTransactions);
until StreamReader.EndOfStream;
StreamReader.Free;
end;
end;
- Rejestracja:ponad 12 lat
- Ostatnio:ponad 12 lat
- Postów:8
Jeżeli SetLength nie kopiuje poprzedniej wartości do nowego miejsca a jedynie zwiększa rozmiar poprzedniego bloku to na 100% jest to wina błędu alokacji ciągłego bloku pamięci w windzie.
Zrób zamiast tej ciągłej tablicy jakąś listę jedno lub dwukierunkową z tych rekordów i na niej działaj.
if (CountTransactions>=MaxTransactions) then
begin
MaxTransactions:=MaxTransactions+AddTransactions;
try
SetLength(Transactions,MaxTransactions);
except
on EOutOfMemory do
begin
Memo1.Lines.Add('Brak pamieci. Zaladowano: '+IntToStr(CountTransactions)+' rekordow.');
Result:=false;
Break;
end;
end;
end;
Cóż za emulacja listy. Wymyślanie koła od nowa to twoja specjalność chyba.
Jeżeli SetLength nie kopiuje poprzedniej wartości do nowego miejsca a jedynie zwiększa rozmiar poprzedniego bloku to na 100% jest to wina błędu alokacji ciągłego bloku pamięci w windzie.
Hmhm. Jaki błąd?
Kolega nie słyszał, że pamięć alokowana dla tablicy (nie mylić z listą) musi znajdować się w bloku ciągłym (pusty obszar) lub rosnąć w nim inaczej po ptokach?
Wiem że musi, ale mi na mojej maszynie udało się zaalokować ~600Mb w bloku ciągłym z pomocą TFPGList<int64>. Przy integerze wywalało przekręcenie licznika listy. Więc nie wiem czy jest potrzeba tworzenia skomplikowanej listy która nie będzie ciągła i przez to wolniejsza.
Teraz już ogarniam o jaki błąd chodzi. Z początku myślałem że chodzi o jakiś bug windy.
-123oho napisał(a):
if (CountTransactions>=MaxTransactions) then
begin
MaxTransactions:=MaxTransactions+AddTransactions;
try
SetLength(Transactions,MaxTransactions);
except
on EOutOfMemory do
begin
Memo1.Lines.Add('Brak pamieci. Zaladowano: '+IntToStr(CountTransactions)+' rekordow.');
Result:=false;
Break;
end;
end;
end;Cóż za emulacja listy. Wymyślanie koła od nowa to twoja specjalność chyba.
Napisałem to ten sposób, żeby znaleźć przy jakiej wielkości alokacji, program się sypie. Oczywiście mogłem zaalokować od razu 5mln. OK napiszę to na liście, ale podejrzewam, że wystąpi podobny błąd. Chodzi mi raczej o generalny problem, mam 4GB RAM, załóżmy, że dostępne mam 2GB, dlaczego z tych 2GB mam dostępne 300MB? Co z resztą.
Mogę zaalokować trochę więcej gdy na początku wpiszę alokację 4.300.000 rekordów. 4.400.400 już nie wpiszę.
procedure TMain.FormCreate(Sender: TObject);
begin
MaxTransactions:={10000;}4300000;
AddTransactions:=100000;
CountTransactions:=0;
Initialize(Transactions);
SetLength(Transactions,MaxTransactions);
end;
Chodzi mi raczej o generalny problem, mam 4GB RAM, załóżmy, że dostępne mam 2GB, dlaczego z tych 2GB mam dostępne 300MB? Co z resztą.
Masz dostępne 300MB jako całość w jednym bloku. Nie zależy to od fizycznej ilości ramu, po prostu Winda 32bit ma 2GB przestrzeni adresowej dla użyszkodnika. W tej przestrzeni masz różne dllki które dzielą przestrzeń na mniejsze bloki. Być może najdłuższy dostępny blok u ciebie wynosi 300Mb. U mnie udało się do 600Mb (2GB ramu fizycznie).
Lista ma to do siebie że każde dodanie nie spowoduje zwiększenia jej rozmiaru w pamięci bo ona ma "zapas".
Jeżeli mimo listy ciągle brakuje ci pamięci to możesz napisać to na paru listach które będą rezerwować oddzielne bloki ciągłe albo po prostu napisać system który nie będzie aż tyle danych czytał.
Po co wywoływać to na tablicy dynamicznej?
Też nie wiem, FPC tego nie chce.

- Rejestracja:ponad 18 lat
- Ostatnio:około 8 lat
- Lokalizacja:Poznań
- Postów:295
Na poczatku napisales ze na bazie trwa to dlugo.
Jaki to silnik?
Ps Nie zrobic niczego szybszego niz baza danych (zalezy od silnika FireBird'a i tego typu domoroslych konstrukcji nie biore pod uwage).
Z tego co widze i tak czytasz plik (baza robi to samo, przyczym czyta np MSSQL po stronach 8kB).
Rozwiazanie na bazie da sie przyspieszyc - napisz tylko co robisz.
Jesli to przetwarzanie danych ala OLAP to od 2012 MSSQL masz do dyspozycji columnstore index.