Brak pamięci - tablice dynamiczne

Brak pamięci - tablice dynamiczne
0

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.

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
0

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...


edytowany 2x, ostatnio: Patryk27
0

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.

Patryk27
Aha, ja to zrozumiałem jako tablica dwuwymiarowa o wymiarach 64 na 5000000, co nie zmienia faktu, że jest to dosyć spora tablica.
Xitami
  • Rejestracja:ponad 20 lat
  • Ostatnio:około rok
0

pokaż jak powstaje ta tablica

0

Alokuję przez SetLength i przy alokacji 3.600.000 rekordu wywołuje mi wyjątek.

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
0

Sprawdź czy na innym memory-managerze też jest ten problem (np.na FastMM4).


0

Alokuję stopniowo co 10.000 rekordów.

0

Może coś powinienem zmienić w Options projektu ze względu na dość nietypowy rozmiar tablicy?

RS
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 12 lat
  • Postów:8
0

A jak wygląda ten rekord? Z fusów ciężko wróżyć. Kod svp.
Pewnie obiekty w tym rekordzie alokują pamięć po utworzeniu, a funkcja sizeof zwraca to co powinna, czyli sumuje rozmiary wskaźników do nich.

Xitami
  • Rejestracja:ponad 20 lat
  • Ostatnio:około rok
0

a nie jest to czasem lokalna zmienna (na stosie)?

0

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?

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
0

Tablice i tak nie są tworzone przecież na stosie...


Patryk27
O ile mi wiadomo, w Delphi tablice o znanych rozmiarach tworzone są na stosie, a za pomocą SetLength na stercie.
abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:7 miesięcy
  • Postów:6610
0
  1. podziel na mniejsze tablice
  2. 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
Kopiuj
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


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
Xitami
  • Rejestracja:ponad 20 lat
  • Ostatnio:około rok
0

no masz, trzyma się stosu jak pijany płota :-)
coś mnie chodzi po głowie, że pierwsze po {$M to stos, ale w 16 bajtowych ramkach, liczby podobne...

0
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).

abrakadaber
abrakadaber
  • Rejestracja:ponad 12 lat
  • Ostatnio:7 miesięcy
  • Postów:6610
0

może zamiast opowiadać tak pięknie pokażesz wreszcie kod


Chcesz pomocy - pokaż kod - abrakadabra źle działa z techniką.
0

Kod jest prostacki: deklaracja rekordu, deklaracja tablicy dynamicznej i SetLength() co 10.000 rekordów, obsługa wyjątku EOutOfMemory.

0

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 ???

0

Nie o błąd w kodzie mi chodzi, bo kod działa prawidłowo. Ale dla tych co chcą kodu proszę:

Kopiuj
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;
RS
Czy próba alokacji całego bloku (bez tej zmiany rozmiaru w pętli) pamięci także kończy się błędem?
RS
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 12 lat
  • Postów:8
0

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.

edytowany 1x, ostatnio: RaveStar
0

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?

RS
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? http://stackoverflow.com/questions/60871/how-to-solve-memory-fragmentation
0

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.

0
-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ę.

Kopiuj
procedure TMain.FormCreate(Sender: TObject);
begin
  MaxTransactions:={10000;}4300000;
  AddTransactions:=100000;
  CountTransactions:=0;
  Initialize(Transactions);
  SetLength(Transactions,MaxTransactions);
end;
Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
0
Kopiuj
Initialize(Transactions);

Po co wywoływać to na tablicy dynamicznej?


1

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.

0

Dziękuję za pomoc. Na liście działa. Rozumiem już w czym problem. Dziękuję.

Xitami
  • Rejestracja:ponad 20 lat
  • Ostatnio:około rok
0

myślę, że nie zrozumieliśmy i nie dotarliśmy
w Pascalu (którymś) alokowałem tablice po prawie 2GB, bo tyle można
i z fizyczną pamięcią RAM miało to związek luźny
a co to Tatusiu pamięć wirtualna? a to coś co wymyślił twój dziadek

crowa
  • Rejestracja:ponad 18 lat
  • Ostatnio:około 8 lat
  • Lokalizacja:Poznań
  • Postów:295
0

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.


Tomasz Andrzejewski
Delphi (XE3-XE7) framework engineer @ InterLan
MCP: Microsoft SQL Server 2008, Implementation and Maintenance
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)