Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Выделение блоков памяти [D7]


Страницы:   1 2

mr.ASH ©   (2007-03-13 11:04:00) 

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

есть структура
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); возникает ошибка, связанная с некорректным освобождением памяти... в чем ошибка, что не так?


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

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

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


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

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

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


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

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

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

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

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


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

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

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

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);


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


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


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


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

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


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

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


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

если у нас на массив ссылается два указателя, мы ведь делаем перемещение его в другой указатель если происходит явное выделение нового блока и копирование в него старого... при этом один корректируется .. а что при этом происходит со 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) - но не наоборот.


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


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


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


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

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

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


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

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


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

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

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

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

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


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


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


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

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

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

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

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

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


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


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


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


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


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


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


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


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

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


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

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

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


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


> Sapersky   (13.03.07 13:05) [9]


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


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


Sapersky   (2007-03-13 13:05:00) [19]

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


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


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



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


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


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


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


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


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


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо