Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2007.04.01;
Скачать: [xml.tar.bz2];

Вниз

Выделение блоков памяти   Найти похожие ветки 

 
mr.ASH   (2007-03-13 11:04) [0]

Привет, уважаемые мастера вот такая возникла проблема:

есть структура
TSomeStruct = record
 V: Variant;
 P: Pointer;
end;

PSomeStruct = ^TSomeStruct;

я хочу создать сразу несколько экземпляров (допустим 10) такой структуры, выделив под них один блок памяти и пото его разрезав на части:

P := AllocMem(10 * SizeOf(TSomeStruct));
for i:= 0 to 10 do begin
 NewStruct := @(integer(P) + i* SizeOf(TSomeStruct));
 ...
end;

или это же можно сделать используя Array [0..0] of TSomeStruct;

вроде все даже работает.. но когда освобождается память
для всех экземпляров TSomeStruct вроде Dispose(NewStruct); возникает ошибка, связанная с некорректным освобождением памяти... в чем ошибка, что не так?


 
Сергей М. ©   (2007-03-13 11:12) [1]


> и пото его разрезав на части


А где ты его собственно "режешь" ?

Строка

NewStruct := @(integer(P) + i* SizeOf(TSomeStruct));

это вовсе не "резка", это просто получение абсолютного адреса неких данных, находящихся по такому-то смещению в ранее распределенном блоке памяти такого-то размера.

Dispose() предназначен прежде всего для освобождения (и, если необходимо, предварительной финализации) блоков, распределенных с пом. New()


 
mr.ASH   (2007-03-13 11:41) [2]

NewStruct := @(integer(P) + i* SizeOf(TSomeStruct));
да это и есть "разрезка", я разделяю блок памяти на экземпляры структуры

Ок.. юзаем не Dispose, а FreeMem, предварительно финализировав вариант.. ошибка все равно остается..


 
Сергей М. ©   (2007-03-13 11:49) [3]


> юзаем не Dispose, а FreeMem .. ошибка все равно остается


А куда она денется ?

Здесь та же самая ситуация, что и с парой New()/Dispose(), с той лишь разницей, что о неявной финализации речи не идет.

Тебе чем TList не угодил ?


 
mr.ASH   (2007-03-13 12:04) [4]

Я хотел сделать что бы память выделялась не под каждый элемент, а для группы сразу. TList не подходит т.к. я делаю что-то вроде THash и это мне нужно что бы сделать пул элементов. Какие есть альтернативы кроме массивов?


 
GrayFace ©   (2007-03-13 12:31) [5]


> Ок.. юзаем не Dispose, а FreeMem, предварительно финализировав
> вариант.. ошибка все равно остается..

Ты выделяешь один блок, а пытаешься освободить 10 маленьких. Например, сохраняй в начале блока кол-во записей, при освобождении уменьшай на 1 и, если он стал = 0, освобождай память.


 
mr.ASH   (2007-03-13 12:38) [6]

Т.е. если я выделяю блок памяти я не могу его потом освободить по частям, а только весь сразу, и вызывая FreeMem для указателя на начало блока, а не к примеру в середину?


 
mr.ASH   (2007-03-13 12:58) [7]

Есть ли способ обойти это и сделать выделение памяти сразу для группы записей??


 
Сергей М. ©   (2007-03-13 13:04) [8]


> mr.ASH   (13.03.07 12:38) [6]



> вызывая FreeMem для указателя на начало блока, а не к примеру
> в середину?


Да, именно так. Иначе и быть не может.


> Есть ли способ обойти это и сделать выделение памяти сразу
> для группы записей?


Так ведь ты и выделяешь память сразу для всей группы)..


> TList не подходит т.к. я делаю что-то вроде THash


Что-то я не понял взаимосвязи между каким-то там THash и TList ..


 
Sapersky   (2007-03-13 13:05) [9]

Не нужно смешивать New/Dispose и GetMem/FreeMem.
В данном случае можно использовать и последнее, но перед FreeMem для каждой записи делать Finalize (из-за наличия в структуре Variant).
Самое простое, ИМХО, array of TSomeStruct + SetLength. Внутри оно работает через тот же самый GetMem/FreeMem, но все финализации выполняются автоматически.


 
Сергей М. ©   (2007-03-13 13:11) [10]


> Sapersky   (13.03.07 13:05) [9]


> из-за наличия в структуре Variant


Не так страшен Variant, как его малюют)
Главное чтобы этот самый Variant в подконтрольной ему структуре не ссылался на иные (распределенные "индивидуально") блоки..


 
mr.ASH   (2007-03-13 13:13) [11]

1. THash не может использовать TList идеологически т.к. для меня он является базовым классом таким же как и TList, к тому же это значительно скажеться на производительности... Это все равно что для Capacity в TList использовать TList :-)

2. Я хочу понять как можно сделать быстрое выделение памяти сразу для 100 PSomeStruct, поскольку выделять по отдельности будет в данном случае не оправданно медленно. Вопрос как организовать пул таким образом что бы он сразу выделил память для X элементов, мог добавлять еще Y, Z элементов в ходе работы если нужно (рости) и корректно их освобождать наиболее оптимальным способом. Я предположил что выделить память при помощи GetMem и потом разделить ее на элементы будет быстрее, но так не получится поскольку ... см. выше. Что придумать что бы все же довести это до ума??


 
mr.ASH   (2007-03-13 13:18) [12]

Да к примеру можно использовать массив, но меня смущает перераспределение памяти при росте пула, если я правильно понимаю внутри SetLength происходит следующее: создается новый блок памяти нужного размера и существующему массиву делается CopyMem в новый блок, а старый освобождается не так ли?


 
Сергей М. ©   (2007-03-13 13:19) [13]


> 1. THash не может использовать TList идеологически т.к.
> для меня он является базовым классом


Бред какой-то ..


> 2. Я хочу понять как можно сделать быстрое выделение памяти
> сразу для 100 PSomeStruct


Так ведь в P := AllocMem(10 * SizeOf(TSomeStruct)); ты как раз и делаешь это !
В чем проблема-то ?


> выделять по отдельности будет в данном случае не оправданно
> медленно


Штатный BMM "заточен" как раз под управление малыми блоками памяти !


 
mr.ASH   (2007-03-13 13:24) [14]

>Бред какой-то ..
Базовым не в смысле наследования, а в смысле он не использует других классов. На эго основе его пула можно будет написать и свой TList.

>В чем проблема-то ?
в освобождении, потому что при таком выделении нужно хранить массив указателей начала выделенных блоков и финализировать все структуры а потом освобождать эти указатели.

>Штатный BMM "заточен" как раз под управление малыми блоками памяти !

Это значит что :

AllocMem(100)
будет медленнее чем
for i:=1 to 100 do
AllocMem(1);
?? :-)


 
Сергей М. ©   (2007-03-13 13:36) [15]


> mr.ASH   (13.03.07 13:24) [14]


> в освобождении


Ну так и освобождай именно те блоки, адреса которых ты получил вызовами New, GetMem, Allocmem, StrAlloc и т.д. и т.п !!! А не те якобы "блоки", адреса которых ты взял черт-те откуда и адреса которых BMM знать не знает !!

В чем проблема-то ? В незнании принципов работы BMM и ему аналогичных ?


> Это значит что :
>
> AllocMem(100)
> будет медленнее чем
> for i:=1 to 100 do
> AllocMem(1);
> ?? :-)


так-с.

закругляй ветку.

Заводи новую под вопросом "как работает BMM".


 
Sapersky   (2007-03-13 14:00) [16]

если я правильно понимаю внутри SetLength происходит следующее: создается новый блок памяти нужного размера и существующему массиву делается CopyMem в новый блок, а старый освобождается не так ли?

SetLength работает в основном через ReallocMem. Которая может выделять новый блок и копировать туда старый, а может перевыделить на месте, как менеджер памяти решит (см. справку). Явное выделение нового блока и копирование в него старого делается только если счётчик ссылок больше 1, что бывает относительно редко. Ну и плюс ещё FillChar нулями выделенных областей и Finalize освобождаемых (см. System.pas, DynArraySetLength).

Собственно, ничто не мешает сделать SetLength сразу на много элементов, с запасом, так же как и c AllocMem.

По моему опыту для относительно небольшой структуры (record) при добавлении элементов в массив с перевыделением большими блоками (например, каждый раз в два раза больше, чем было) массив быстрее, чем список (хотя и не намного; зависит ещё от версии Delphi, в BDS2006 TList заметно ускорился, видимо, из-за FastMM).


 
mr.ASH   (2007-03-13 14:04) [17]

Проблема в том как это сделать выделение блока памяти для списка записей не используя при этом ни TList, ни массивы или прочие более высокоуровневые вещи, а потом еще и высвобождение (ну и где хранить список тех указателей, которые нужно освободить?? может быть в массиве или TList`е??). И еще проблема в том что ты совершенно не понял вопрос потому что я наверное не внятно описал проблему...  Извиняюсь что отнял время и спасибо за ответы..


 
mr.ASH   (2007-03-13 14:13) [18]

>>Sapersky
 Огромное спасибо...  одни нюанс
> если счётчик ссылок больше 1
если у нас на массив ссылается два указателя, мы ведь делаем перемещение его в другой указатель если происходит явное выделение нового блока и копирование в него старого... при этом один корректируется .. а что при этом происходит со 2м??

>Собственно, ничто не мешает сделать SetLength
Пожалуй именно так и поступлю.. просто надеялся что есть более "продвинутые" и более оптимальные решения.. еще раз спасибо..


 
Сергей М. ©   (2007-03-13 14:55) [19]


> есть более "продвинутые"


Что есть "оно", по твоему мнению ?


 
Sapersky   (2007-03-13 15:07) [20]

если у нас на массив ссылается два указателя, мы ведь делаем перемещение его в другой указатель если происходит явное выделение нового блока и копирование в него старого... при этом один корректируется .. а что при этом происходит со 2м??

Второй остаётся указывать на старый блок.
Вообще это всё написано в справке и вот здесь:

http://www.rsdn.ru/article/Delphi/dynarrays.xml

Если хочется иметь две переменных-массива без подобных эффектов с копированием, можно делать присвоение с приведением типов Pointer(arr2):= Pointer(arr1). Разумеется, при изменении arr1 придётся вручную корректировать arr2, к тому же надо не забыть обнулить arr2 перед выходом из области видимости: Pointer(arr2):=nil, иначе заботливый компилятор уничтожит ещё, возможно, нужный вам массив.

Можно попробовать сделать вторую переменную указателем на первый массив - ^(array of TSomeStruct), хотя не знаю, что из этого получится, не пробовал (т.е. будут ли нормально проходить обращения к элементам).

Также можно безболезненно кастить array of TSomeStruct к ^(Array [0..0] of TSomeStruct) - но не наоборот.


 
mr.ASH   (2007-03-13 15:27) [21]

>Sapersky
Супер мега спасибо..!!


 
mr.ASH   (2007-03-14 12:29) [22]

В общем помучавшись я понял, что через массив тоже не получится, потому что при перевыделении массива может поменяться его адрес в памяти и следовательно все указатели на его элементы станут не действительными.. так что тут либо New-Dispose либо массив массивов нужно делать..


 
Сергей М. ©   (2007-03-14 12:44) [23]


> при перевыделении массива может поменяться его адрес в памяти


Может, но только в случае если новый размер больше старого


 
mr.ASH   (2007-03-14 13:01) [24]

А поскольку пул в основном ростет, то так оно и будет. Применение массива для создания сразу нескольких элементов пула не корректно, если использовать один массив и постоянно его увеличивать... Выход - использовать массив массивов...

Ни как не могу разобраться как правильно инициализировать структуру

P := AllocMem(10 * SizeOf(TSomeStruct));
for i:= 0 to 10 do begin
NewStruct := @(integer(P) + i* SizeOf(TSomeStruct));
Initialize(NewStruct^); // Верна ли такая инициализация?
...
end;
....
NewStruct using
....
FreeMem(P);


 
Sapersky   (2007-03-14 15:24) [25]

при перевыделении массива может поменяться его адрес в памяти и следовательно все указатели на его элементы станут не действительными..

Можно использовать индексы в массиве вместо указателей. Правда, при удалении элементов обычным методом - со сдвигом "хвоста" - индексы элементов этого "хвоста" будут меняться; но можно удалять и по-другому, например, если не требуется сохранение порядка элементов - перемещать последний элемент на место "дырки".

Initialize(NewStruct^); // Верна ли такая инициализация?

AllocMem заполняет нулями выделенную память, больше никакой инициализации не требуется. Финализация - да, если в Variant были записаны типы, которым она нужна (длинные строки, дин. массивы, интерфейсы).


 
Sapersky   (2007-03-14 15:30) [26]

можно удалять и по-другому, например, если не требуется сохранение порядка элементов - перемещать последний элемент на место "дырки".

То есть нет, конечно, тогда изменится индекс последнего элемента.
Ну тогда только помечать "дырки" при удалении и заполнять их при добавлении элементов.


 
mr.ASH   (2007-03-14 15:41) [27]

>Можно использовать индексы в массиве вместо указателей. Правда, при >удалении элементов обычным методом - со сдвигом "хвоста" - индексы >элементов этого "хвоста" будут меняться; но можно удалять и по-другому, >например, если не требуется сохранение порядка элементов - перемещать >последний элемент на место "дырки".

Хм.. это немного не то, т.к. для получения элемента нужно будет разыменовать массив по этому индексу, что будет медленне чем просто разыменовать указатель не так ли? Т.е. мы оптимизируем пул, но работа самого хеша замедляется! Но все равно спасибо!



Страницы: 1 вся ветка

Форум: "Начинающим";
Текущий архив: 2007.04.01;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.042 c
1-1170926314
kilop
2007-02-08 12:18
2007.04.01
как сделать так чтобы при запуске форма появлялась в центре


1-1171015510
DelphiLexx
2007-02-09 13:05
2007.04.01
FreeAndNil


2-1173828969
Fantasy
2007-03-14 02:36
2007.04.01
Странно но факт. помогите понять


2-1173258178
sergeyst
2007-03-07 12:02
2007.04.01
Как убрать слэш из пути


2-1173306689
Ezorcist
2007-03-08 01:31
2007.04.01
сколько в памяти займет integer ?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский