Войти
ДельфинарийФорум

выделение памяти под много объектов

#0
1:52, 11 янв. 2008

начитался всяких статеек и сделал так -

- создаю большусчий статик массив[0..n]объектов, выделяю большой кусок памяти под все инстанцы класса и уже в нем втыкаю экземпляры класса.

- допустим реальные объекты делятся на несколько суб-классов по типу поведения. при необходимости создать реальный объект, инициализирую поля элемента большого массива и делаю плохой хак, чтобы превратить его в объект нужного суб-класса.


кусок кода:

// классы
type
  tobj = class
  end;
  tnewobj = class(tobj)
  end;

...
// распределение массива
var
  obj : array[0..n]of tobj;
  p : pbyte;

getmem(p, InstanceSize*n);
for i:=0 to n-1 do begin
  obj[i] := tobj.create;
  inc(p, InstanceSize);
end;

...
// изменение типа
type
  pclass = ^tclass;

pclass(obj[i])^ := tnewobj;
все, теперь obj - является экземпляром класса tnewobj

я не очень четко представляю себе внутреннее устройство классов, механизмы создания и удаления вмт и т.п. (обещаю исправиться asap :)
поэтому пока есть вопросы:
1. что плохого можно с этого поиметь?
2. что хорошего можно с этого поиметь? =)
3. будут ли лики при использовании хака с изменением типа? (там, где читал, чел честно признался - мол "создается новая вмт, памяти юзается больше, но это, типа не важно")


#1
11:34, 11 янв. 2008

1. Зависит от реализации объектной модели в компиляторе. Как минимум всякие несовместимости (с компилятором, платформами и т.п.)
2. Че-то не догоняю ;)
3. В рантайме никаких VMT не создается. Причин для ликов вроде нет

Вопрос такой:
Зачем делать хак, когда можно сразу создавать объект нужного класса, с переопределенным аллокатором? Для этого нужно лишь наследовать все классы, для которых надо свое распределение памяти от одного базового, где собственно и будет переопределен аллокатор. Хотя можно даже не наследовать, если нет возможности.

Ну и наконец, уверен, что боттлнек в выделении памяти? Дельфийский менеджер выделяет маленькие блоки очень эффективно.

#2
16:18, 11 янв. 2008

>Зачем делать хак, когда можно сразу создавать объект нужного класса, с переопределенным аллокатором?
можно, я же просто экспериментирую.
аллокатор должен знать, где первый свободный адрес в большом блоке, соответственно, надо еще делать менеджмент этого блока, а хак - удобно, ничего переопределять не надо, чик - и в дамки =)

>2. Че-то не догоняю ;)
ну как - все умные большие программеры наперебой талдычат - "по возможности выделяй память большим блоком, не удаляй, не перераспределяй, реюзай", вот и думаю - может это что-то дать, или нет.

#3
17:58, 11 янв. 2008

По-моему что с хаком, что без оного надо менеджить, иначе как размещать? А менеджер в модуле соответствующем.

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

Это для С++ актуально скорее.

#4
18:37, 11 янв. 2008

>По-моему что с хаком, что без оного надо менеджить, иначе как размещать? А менеджер в модуле соответствующем.
с хаком я менеджу один раз на старте, дальше просто реюзаю уже готовые объекты.
допустим, у каждого из размещенных в массиве объектов есть флажок memused, если надо новый объект, то действую так:

for i:=0 to n-1 do // вместо цикла конечно можно сделать связный список свободных блоков, чтобы еще ускорить
  if not obj[i].memused then begin // ищу первый свободный
    pclass(obj[i])^ := tnewobj; // привожу его к нужному типу
    obj[i].init(...); инициализирую поля (без аллокации, т.е. участок памяти остается тем же?)
  end;
дальше я могу с obj работать как с tnewobj.
если объект надо уничтожить, по необходимости чищу внутренние поля и ставлю memused = false, чтобы его можно было использовать.

#5
19:17, 11 янв. 2008

А что если instance size у субклассов разный и не такой как у базового? Тогда все посыпется.

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

Вообще прежде чем писать замену чему-то стандартному, имеет смысл ознакомиться с тем, как оно написано. Вдруг уже как надо? ;)

Через переопределение NewInstance можно просто создаваемому штатными средствами объекту сразу нужного класса, указать место, где он будет размещаться (собственно так и осуществляется размещение объектов в памяти).
Я так сообщения распределяю в пуле, т.к. мне нужна максимальная скорость и что-то вроде сборщика мусора для сообщений. Т.е. их не надо освобождать.

#6
20:01, 11 янв. 2008

>А что если instance size у субклассов разный и не такой как у базового? Тогда все посыпется.
пока что тестю классы, которые отличаются только методами.
потестил через многократное создание удаление нескольких миллионов экземпляров, вроде пока стабильно, av никто не кидается.
надо будет еще на лики тщательнее потестить.

>Через переопределение NewInstance можно просто создаваемому штатными средствами объекту сразу нужного класса, указать место, где он будет размещаться (собственно так и осуществляется размещение объектов в памяти)
так и делаю, в NewInstance базового класса что-то вроде этого -

...
  result := InitInstance(marker); // marker - это указатель p из первого поста, пробегающий выделенный большой блок
...

>Вообще прежде чем писать замену чему-то стандартному, имеет смысл ознакомиться с тем, как оно написано. Вдруг уже как надо? ;)
стандартный аллокатор вызывает getmem для каждого instance, может будет лучше вызвать его один раз для всех объектов? =)

#7
20:55, 11 янв. 2008

> так и делаю, в NewInstance базового класса что-то вроде этого -

А хак тогда нафига? Оно и так создастся того класса, которым создается, а не того, которым ты массив забил. Забивание массива экземплярами базового коасса вообще лишнее. Достаточно просто память выделить.

> стандартный аллокатор вызывает getmem для каждого instance, может будет лучше вызвать его один
> раз для всех объектов? =)

Ну GetMem это не malloc. Он делает тоже, что и ты. Т.е. берет у системы память большими блоками и распределяет по кускам. Причем в FastMM (который интегрирован начиная с 2006 версии) для маленьких блоков используются какие-то суперские оптимизации (за счет памяти вроде).
Если ты не планируешь использовать разные размеры инстансов, то может и есть шанс написать что-то более быстрое. А универсальный менеджер быстрее FastMM написать... Ну это гуру оптимизации надо быть.:)

#8
21:41, 11 янв. 2008

>А хак тогда нафига? Оно и так создастся того класса, которым создается, а не того, которым ты массив забил.
чтобы список свободных блоков не менеджить. рождение объекта вполне локально по времени, поэтому можно просто пробежать по статичному заранее забитому массиву, найти первый свободный объект, переключить его тип и использовать. можно также попробовать переключать тип объектов уже во время работы.

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

ДельфинарийФорум

Тема в архиве.