Форум: "Основная";
Текущий архив: 2004.08.01;
Скачать: [xml.tar.bz2];
ВнизНе хватает памяти. Найти похожие ветки
← →
opoloXAI (2004-07-12 12:02) [0]Добрый день.
На Celeron400 с 65МГбт свободной памяти я динамически увеличиваю размер массива следующего типа:
type
TMyMassiv = record
v1: String;
v2: String;
v3: Integer;
end;{record}
var
mas: array of TMyMassiv;
Увеличиваю количество элементов массива на один и добавляю новый элемент. При достижении числа элементов массива около 4000..5000 сбодной памяти остается 0 Кбт. Дальше работа программы превращается в полные "тормоза".
Вероятнее всего я нерационально отнесся к памяти, наверное стоило воспользоваться TStream. Но я не знаю как пользоваться структурой TStream of TMyMassiv. Подскажите примерчиком кода.
Заранее спасибо.
← →
Тимохов © (2004-07-12 12:12) [1]не зная стреднюю длину строк v1 и v2 сказать что-то сложно.
если длина примерно 30 символов, то опиасанный вами массив будет занимать 400кб, что согласитесь копейки.
← →
Тимохов © (2004-07-12 12:14) [2]"полные" тормоза у вас скорее в другом месте программы
← →
MBo © (2004-07-12 12:14) [3]>Увеличиваю количество элементов массива на один
Вот здесь и собака порылась - стоит увеличивать размер на существенное число элементов. Или воспользоваться списками TList, а может, и TStringList еще удобнее будет.
← →
Sheng © (2004-07-12 12:21) [4]У меня тоже были глюки с динамическими массивами. Я решил это просто - использовал стек. Скорость сразу, как говориться, на глаз раза в 4 быстрее стала, и работает устойчиво. Вот те пример кода:
PMyMassiv = ^TMyMassiv;
TMyMassiv = record
v1: String;
v2: String;
v3: Integer;
Next: PMyMassiv;
end;
...
MyMassivFirst,MyMassiv: PMyMassiv;
дальше пишешь процедуры добавления, поиска и очистки стека:
procedure TfrmMain.AddToStack(<твои переменные для добавления> var sMyMassiv: PSongs);
begin
if sMyMassiv=nil then New(sMyMassiv) else
begin
New(sMyMassiv^.Next);
sMyMassiv:=sMyMassiv^.Next;
end;
sMyMassiv^.<тут присваиваешь>
sMyMassiv^.<тут присваиваешь>
...
sMyMassiv^.Next:=nil;
end;
procedure TfrmMain.KillStack(var sMyMassiv: PMyMassiv);
var
Temp: PMyMassiv;
begin
while sMyMassiv<>nil do
begin
Temp:=sMyMassiv^.Next;
StrDispose(sMyMassiv^.Name);
Dispose(sMyMassiv);
sMyMassiv:=Temp;
end;
end;
если чего не понятно - пиши
← →
Тимохов © (2004-07-12 12:29) [5]
> StrDispose(sMyMassiv^.Name);
вы тут вероятно имели и виду v1 and v2?
зачем это?
при dispose записи строки-элементы записи будут очищены автоматически.
← →
Тимохов © (2004-07-12 12:31) [6]динамические массивы работают плохо в одном случае - если в массив добавлять много элементов вот так:
SetLength(a, length(a)+1). В этом случае каждый раз вызывается reallocmem. Лучше всего брать механизм реализованный в TList: count + capacity. Capacity просто равно текущей длинне массива. А count придется хранить отдельно. Скорость добавления возрастает многократно. По опыту добавления 100000 элементов - примерно в 100 раз.
← →
Sheng © (2004-07-12 13:07) [7]
> Тимохов © (12.07.04 12:29) [5]
>
> > StrDispose(sMyMassiv^.Name);
>
> вы тут вероятно имели и виду v1 and v2?
сорри, не всё покоцал. Это я пример из проги своей тянул. Конечно же, не надо.
← →
opoloXAI (2004-07-13 11:11) [8]Добрый день.
Я понял, что использовать динамические массивы, в принципе, никто не советует. Но всё-же хотелось бы для себя понять (дабы в следующий раз "не наступить на те-же грабли"), почему приведённый код на Win98 съедает 60МГбт памяти за считанные секунды?
TmySearchRec = record
Size: Integer;
Name: TFileName;
Path: String;
end;
TSameForm = class(TForm)
DirectoryLB: TdfsSystemTreeView;
StBar: TStatusBar;
ItemFind: TMenuItem;
private
{ Private declarations }
mas: array of TmySearchRec;
kolFls: Int64;
GetPamat: Int64; {специально, чтобы подсчитатать размер занимемой памяти}
procedure ScanDir(StartDir: string);
procedure AddFile(SchRec: TSearchRec; Dir: String);
public
{ Public declarations }
end;
var
SameForm: TSameForm;
function myFileSize(sz: Int64): String;
function Okruglit(x: Real; nm:Word): String;
function Stepen(y,x:Real): Real;
implementation
{$R *.dfm}
function myFileSize(sz: Int64): String;
var ms: Real48;
begin
ms:=sz;
Result:=FloatToStr(ms)+" байт";
ms:=ms/1024;
If ms>=1 then Result:=Okruglit(ms,3)+" Кбайт";
ms:=ms/1024;
If ms>=1 then Result:=Okruglit(ms,3)+" Мбайт";
ms:=ms/1024;
If ms>=1 then Result:=Okruglit(ms,3)+" Гбайт";
end;
function Okruglit(x: Real; nm:Word): String;
var s: String;
tmp: Real;
i,p: Word;
begin
tmp:=Round(x*Stepen(10,nm))/Stepen(10,nm);
s:=FloatToStr(tmp);
p:=pos(",",s);
If p=0 then begin
s:=s+",";
p:=length(s);
end;
For i:=1+length(s)-p to nm do s:=s+"0";
Result:=s;
end;
function Stepen(y,x:Real): Real;
begin
Result:=exp(x*ln(y));
end;
procedure TSameForm.ItemFindClick(Sender: TObject);
begin
kolFls:=0;
SetLength(mas,kolFls);
try ScanDir(DirectoryLB.Directory);
except MessageDLG("Ошибка обращения к диску !",mtWarning,[mbYes],0);
end;
end;
procedure TSameForm.ScanDir(StartDir: string);
var SearchRec: TSearchRec;
begin
If StartDir[Length(StartDir)]<>"\" then StartDir:=StartDir+"\";
If FindFirst(StartDir+"*.*",faAnyFile,SearchRec)=0 then begin
repeat If (SearchRec.Attr and faDirectory)<>faDirectory then AddFile(SearchRec,StartDir)
else If (SearchRec.Name<>"..")and(SearchRec.Name<>".") then ScanDir(StartDir+SearchRec.Name+"\");
until FindNext(SearchRec)<>0;
FindClose(SearchRec);
end;
StBar.Panels[2].Text:=" "+StartDir;
StBar.Panels[1].Text:=" Найдено : "+IntToStr(kolFls)+" файла(ов)";
StBar.Panels[0].Text:=" Память: "+myFileSize(GetPamat);
Application.ProcessMessages;
end;
procedure TSameForm.AddFile(SchRec: TSearchRec; Dir: String);
begin
kolFls:=kolFls+1;
SetLength(mas,kolFls);
mas[kolFls-1].Size:=SchRec.Size;
mas[kolFls-1].Name:=AnsiUpperCase(SchRec.Name);
mas[kolFls-1].Path:=Dir;
GetPamat:=GetPamat+SizeOf(SchRec.Size)+SizeOf(SchRec.Name)+SizeOf(Dir);
end;
Результат работы таков: когда найдено 12065 файлов свободной физической памяти нет вообще (из 80МГбт возможных), а размер файла подкачки составляет 65МГбт. Я специально ввел переменную GetPamat, чтобы считать, сколько занимается на хранение данных массива. Получается 141,387Кбт. Так куда же девается память? Может я где-то забыл поставить ... .Free???
Заранее спасибо.
← →
han_malign © (2004-07-13 11:27) [9]Что такое фрагментация памяти знаешь?
Под каждую строку выделяется блок памяти, под динамический массив, каждый раз выделяется непрерывный блок памяти, а в старую "дыру" он уже не влезает - вот и получается, что при SetLength(...,Length()+1), занимается память на 2*Length()+1(по грубым прикидкам), и это нарастает в геометрической прогрессии...
← →
Ega23 © (2004-07-13 11:33) [10]Я бы воспользовался TList.
public
List:TList; // объявил список, создать его в OnFormCreate,
procedure TSameForm.AddFile(SchRec: TSearchRec; Dir: String);
var
p:PmySearchRec
begin
New(P);
P^.Size:=SchRec.Size;
P^.Name:=AnsiUpperCase(SchRec.Name);
P^.Path:=Dir;
List.Add(P); GetPamat:=GetPamat+SizeOf(SchRec.Size)+SizeOf(SchRec.Name)+SizeOf(Dir);
end;
Только не забудь на FormDestroy сделать Dispose всем элементам списка.
← →
Тимохов © (2004-07-13 12:45) [11]
> han_malign © (13.07.04 11:27) [9]
> при SetLength(...,Length()+1), занимается память на 2*Length()+1(по
> грубым прикидкам)
да...
прикидки и правда грубые.
Вы это сами придумали?
> Что такое фрагментация памяти знаешь?
А вы знаете? Вы знаете как работает с фрагментами манагер памяти дельфи?
← →
ASMiD (2004-07-13 12:54) [12]По моему печальному и не очень опыту работы с памятью самый быстрый и достаточно надежный способ - это хапнуть много и сразу. Ведь можно же сделать предварительные прикидки - тут уже звучали и 140к и 400к это ничто в сравнении с 65М я уж молчу про файл подкачки.
← →
Тимохов © (2004-07-13 12:55) [13]Автору.
Я лично не говорил, что не советую пользоваться динамическими массивами. По скорости доступа с ними никакой tlist не сравнится. А при реализации механизма count+capacity (как в tlist), то и по скорости добавления не сравнится.
ЧЕстно говоря я не вижу ошибки. Вы говорите, что вы смотрите на сильное потребление памяти в процессе работы? Тогда может быть все дело в том, что сильно глубокая рукурсия? Большая глубина каталогов?
← →
Тимохов © (2004-07-13 12:57) [14]Автору.
Вы неверно оцениваете потребляемую память
sizeof(string) = 4. Надо брать length(string)
← →
Тимохов © (2004-07-13 13:06) [15]
> Тимохов © (13.07.04 12:57) [14]
вообще говоря вернее так: потребляемую память на строку надо оценивать так
length(s) + 4{указатель на строку}+8{служебная инфа строки}+4{служебная инфа блока менеджера памяти дельфи }+1{возможно на #0 в конце}
т.е. длина строки + 17байт, если строка не пустая.
← →
Igorek © (2004-07-13 13:10) [16]
> Тимохов © (13.07.04 12:45) [11]
> > Что такое фрагментация памяти знаешь?
> А вы знаете? Вы знаете как работает с фрагментами манагер
> памяти дельфи?
А вы? :-)
Если я правильно догадываюсь, то поскольку каждому приложению выделяется виртуальное пространство, то непрерывный блок памяти в этом пространстве может совсем не быть таковым в памяти оперативной. Я прав?
← →
Тимохов © (2004-07-13 13:14) [17]
> то непрерывный блок памяти в этом пространстве может совсем
> не быть таковым в памяти оперативной. Я прав?
не знаю.
это особенность реализации уже не менеджедра памяти дельфи, а самой ОС windows. А они это не документируют.
Вас это имхо волновать не должно.
Ваш вопрос не относится ни в коей мере к теме "Фрагментация памяти в дельфи" :р) Задайте что-нибудь более конкретное.
← →
opoloXAI (2004-07-13 13:20) [18]
> Тогда может быть все дело в том, что сильно глубокая рукурсия?
> Большая глубина каталогов?
Получается, что проблема совсем не в динамическом массиве, и проблему "пожерания памяти" мне просто не решить?
← →
Тимохов © (2004-07-13 13:21) [19]в димамический массивах проблемы вообще нет.
вы на вопрос не ответили...
← →
opoloXAI (2004-07-13 13:24) [20]Если Вы о большой глубине катклогов, то я бы ответил, что она совсем не большая. Машина стоит на работе 1,2 ГБт занято на диске музыкой и рабочей требухой (не Windows\Temporary...). Вложеность не сильно большая. !!??
← →
Тимохов © (2004-07-13 13:27) [21]
> Вложеность не сильно большая. !!??
не сильно.
← →
Тимохов © (2004-07-13 13:29) [22]Автору.
Вы перечитайте мои советы по поводу оченки занимаемой памяти под строки.
Может у вас реально занимается очень много.
Т.к. в своих оценках вы вообще игнорировали длинну строк, то может оказаться, что действительно занимается 65мб, т.к. например есть дикректоря с длинющими именами файлов.
посмотрите.
← →
Igorek © (2004-07-13 13:33) [23]
> Тимохов © (13.07.04 13:14) [17]
> Ваш вопрос не относится ни в коей мере к теме "Фрагментация
> памяти в дельфи" :р) Задайте что-нибудь более конкретное.
Простите. Как работает менеджер памяти Дельфи в двух словах? А то я думал что его вообще нету.
← →
Тимохов © (2004-07-13 13:36) [24]
> Дельфи в двух словах?
http://rsdn.ru/article/Delphi/memmanager.xml
только уговор - пока не переварите не делайте заявлений, ок? :)))
← →
opoloXAI (2004-07-13 13:38) [25]К сожалению на работе Delphi не стоит, смогу проверить только вечером дома. Пока спасибо, дома посмотрю.
Тогда вопрос по "лик-безу": я описываю тип:
TmySearchRec = record
Size: Integer;
Name: TFileName;
Path: String;
end;
Может стоить сделать по-другому:
1) если бы не было пупи к файлу, я бы точно съэкономил место?
2) тип String, наверное, стоит описать как String[255]?
3) тип TFileName это ведь тоже String, может его стоить описать как String[25]?
← →
Тимохов © (2004-07-13 13:41) [26]
> opoloXAI (13.07.04 13:38) [25]
совет. не гадайте пока.
проверьте дома сколько все-таки памяти получается.
string[255] всегда будет занимать 256 байт, тогда как string только сколько нужно + накладные расходы.
не мудрите. выполните снаяала совет в начеле ответа.
← →
Igorek © (2004-07-13 13:43) [27]
> Тимохов © (13.07.04 13:36) [24]
> только уговор - пока не переварите не делайте заявлений,
> ок? :)))
железно! :-)
← →
Igorek © (2004-07-13 13:44) [28]Удалено модератором
← →
Anatoly Podgoretsky © (2004-07-13 13:52) [29]Тимохов © (13.07.04 13:06) [15]
+21
← →
ASMiD (2004-07-13 13:55) [30]А я тут подумал - а если путь занимает больше 255 байт - что будет тогда? По-моему полный и непредсказуемый бардак.
← →
Тимохов © (2004-07-13 14:26) [31]Прокомментирую ответ 29 - поправку моего ответа 15.
Действительно для оценки занимаемой памяти непустой строкой к длинне строки надо прибавлсять 21, а не 17 байт.
← →
han_malign © (2004-07-13 14:45) [32]>А вы знаете? Вы знаете как работает с фрагментами манагер памяти дельфи?
- я то как раз знаю...
>Тогда может быть все дело в том, что сильно глубокая рукурсия?
- а вы знаете как работает рекурсия?
При "сильно глубокой рекурсии", будет Stack overflow. И уж стек никогда не свопируется и тормозов, сверх обычных, давать не может.
>+4{служебная инфа блока менеджера памяти дельфи }
- тогда выровнять на размер 4, и еще +4, поскольку флаг/размер повторяется на конце блока, со смещением кратным 4. Еще, тогда уж, не стоит забывать про двусвязный список информации о непрерывных блоках.
>потребляемую память на строку надо оценивать так
- если учитывать, что размер пути обычно(а для не NTFS всегда) не превышает 260(Windows.MAX_PATH), то 12000 записей это - 12000*(6+260+2*21(возьмем цифру от AP))=3696000 - так что считай не считай, а 141MБ не получится.
← →
Тимохов © (2004-07-13 14:49) [33]
> han_malign © (13.07.04 14:45) [32]
ладно, не возмущайтесь :)))
при сильно глубойкой рекурсии в стеке может быть и 4 байта под строку, а сама строка будет в штатном манагере памяти. Если вы возьмете строку длинной 100000 и будете ещее передаваь и в рекутсии и менять внутри, то получите именно конец пасмяти, а не стек оверфлов.
> стоит забывать про двусвязный список информации о непрерывных
> блоках.
Думаю его не надо брать во внимание. Т.к. свободные блоки имеют обыкновение объекдиняться и часто их не так много.
ЗЫ. Все вышесказанное про штатный манагер памяти - getmem.int.
← →
Тимохов © (2004-07-13 14:51) [34]Хочу еще раз дополнить информацию о том как считать сколько занимает памяти непустая строка.
Как я сейчас выяснил - в д5 и д6 разная реализация длинных строк: в д5 длинна служебного блока строки равна 12 байт, в д6 - 8.
Поэтому в д5 накладных расходов на хранение строки + 22
В д6 + 18.
← →
Тимохов © (2004-07-13 14:55) [35]
> han_malign © (13.07.04 14:45) [32]
И вообще не обижайтесь.
Просто ваша формала
length(s)+1 = 2*length(s) + 1 весьма поспешна.
Дело в том, что setlength идут не подряд. Они перемижаются с работой со строками, которые тоже идут из манагера памяти. Поэтому, что там творится в манагере никому неизвестно. Может там полно огромных свободных блоков?
Да, согласен, что опыт показывает, что при последовательном увеличении памяти памяти жрестя больше чем нужно. Но согласитель, что не в 100 раз и это вряд ли может служить причиной ошибки, заявленной автором.
← →
opoloXAI (2004-07-14 16:33) [36]Добрый день.
Может ли кто-нибудь у себя откомпилировать (я делал на Delphi6) и проверить на Win98 следующий код:
procedure TForm1.Button1Click(Sender: TObject);
type TmySearchRec = record
Size: Integer;
Name: TFileName;
Path: String;
end;
var i: Integer;
kolFls,GetPamat: Int64;
mas: array of TmySearchRec;
begin
kolFls:=0;
GetPamat:=0;
SetLength(mas,kolFls);
For i:=0 to 10000 do begin
kolFls:=kolFls+1;
SetLength(mas,kolFls);
mas[kolFls-1].Size:=123456;
mas[kolFls-1].Name:="proba.txt";
mas[kolFls-1].Path:="C:\Work\Delphi";
GetPamat:=GetPamat+SizeOf(mas[kolFls-1].Size)
+length(mas[kolFls-1].Name)+18
+length(mas[kolFls-1].Path)+18;
Label1.Caption:=IntToStr(i);
Label2.Caption:=IntToStr(GetPamat);
Application.ProcessMessages;
end;
SetLength(mas,0);
end;
При работе программы запустить "Системеный монитор" и посмотреть "Свободная физическая память" и "Занято в файле подкачки". Посмотреть, что твориться. На моей здешней (на работе) машине по окончании работы программы свободной памяти 0 МГбт, подкачка - 60 МГбт. Может у меня на самом деле проблемы с этой машиной а не с Delphi? А как у Вас?
← →
Тимохов © (2004-07-14 17:01) [37]вообще говоря к концу цикла по данным диспетчера задач у меня занимает 120мб памяти.
при этом если ПОСЛЕ отработки цикла сделать minimize/maximaize окна, то по данным того же диспетчера приложение займет 2мб. Т.е. объединит и освободит лишние фрагменты.
При этом если добавлять не по 1, а выделять сразу все (или хотябы по многу, например по 1000 штук), то такого не будет.
Посмотрите в TList count+capacity - сделате так.
Готов признать, что в данном вопросе han_malign © скорее прав, чем не прав. Прошу прощения.
ЗЫ. Все-таки, чтобы немного оправдаться перед han_malign © скажу, что если внутри цикла вставить активную и независимую работу с манагером (строки, дин массивы, классы и т.д.), то такого эффекта не наблюдается. Т.е. мое отверждение про то, что манагер умеет объединять блоки и не всегда можно утвержадать про фрагментацию не лишено смысла.
Но еще раз в данном случае han_malign © прав.
← →
Тимохов © (2004-07-14 17:25) [38]я тут еще покапался.
если убрать строчки
mas[kolFls-1].Size:=123456;
mas[kolFls-1].Name:="proba.txt";
mas[kolFls-1].Path:="C:\Work\Delphi";
то нет никаких 120мб, только 2мб.
ясно, что эти строчки приводят к физическому копированию строки (это показал cpu). Но тогда не ясно почему же такая разница.
АВТОРУ.
К сожалению, нет времени глубже копать.
Поэтому совет однозначный:
1. Либо не пользоваться дин. массивом - использовать tlist.
2. Либо пользщоваться, но реализовать для дин. массива тот же подход, что и в TList: count + capacity (см. в исходниках tlist)
← →
Pavelkq (2004-07-15 16:13) [39]Можно использовать TStringList для добавления, а потом сразу опредлив размер дин.массива, как TStringList.Count-1, в цикле перебросить данные. Получится 2 прохода, но все равно экономно и быстро.
← →
MacroDenS © (2004-07-15 16:44) [40]А на мой взгляд,
автор при объявлении:
type
TMyMassiv = record
v1: String;
v2: String;
v3: Integer;
end;{record}
использует переменные типа стринг неопределенной длины!
а такая строка может занимать и до 2Гбт.
Если у тебя строки не очень длинные ( в пределах 255 символов), то лучше задай так:
TMyMassiv = record
v1: String[255];
v2: String[255];
v3: Integer[255];
end;{record}
и тогда ты с точностью до байта сможешь узнать размер одного элемента массива и проще будет отследить расходы памяти.
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2004.08.01;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.033 c