![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Снова выравнивание памяти :)
Riply © (2008-05-08 21:14:00)Здравствуйте !
Учитывая горький опыт предыдущей программистской жизни,
решила, что начинать новую надо с модуля, выравнивающего память :)
Вроде ничего сложного, но при ReallocMem у меня получается
двойное копирование: первый раз копирует память сама ReallocMem, а потом
уже я, если это необходимо.
Как можно избавиться от этого, или уменьшить "частоту" случаев, необходимо копирование ?
Только не советуйте переписать SysReallocMem из getmem.inc :)
Вот что у меня пока получается:
type
PMEM_STUFF = ^_MEM_STUFF;
_MEM_STUFF = packed record
{$IFDEF SH_MEM_128}
_Stuff: array[0..7] of Byte;
{$ENDIF}
Stuff: Int64;
end;
type
PMEM_HEADER = ^_MEM_HEADER;
_MEM_HEADER = packed record
_Shift: Word;
_HeaderSize: Word;
_RealPtr: Pointer;
end;
type
PMEM_BLOCK = ^_MEM_BLOCK;
_MEM_BLOCK = packed record
Length: LongWord;
MaxLength: LongWord;
_Shift: Word;
_HeaderSize: Word;
_RealPtr: Pointer;
end;
const
{$IFDEF SH_MEM_128}
__PRED = SizeOf(Int64) shl 1 - 1;
__SHIFT = - (SizeOf(Int64) shl 1);
{$ELSE}
__PRED = SizeOf(Int64) - 1;
__SHIFT = - SizeOf(Int64);
{$ENDIF}
__MEM_SIZE_INC = SizeOf(_MEM_STUFF) + SizeOf(_MEM_BLOCK);
{$IFDEF SH_MEM_MNG}
var
__MemMgrEx: TMemoryManagerEx;
{$ENDIF}
function ROUND_UP_DEFINE(const Value: LongWord): LongWord; inline;
begin
Result := (Value + __PRED) and __SHIFT;
end;
function OFFSET_TO_DEFINE(const P: Pointer): LongWord; inline;
begin
{$IFDEF SH_MEM_128}
Result := OFFSET_TO128(LongWord(P));
{$ELSE}
Result := OFFSET_TO64(LongWord(P));
{$ENDIF}
end;
function _LMem_GetRealPtr(const pBlock: PMEM_BLOCK; var P: Pointer): NTSTATUS; inline;
begin
P := pBlock._RealPtr;
if P = Pointer(LongWord(pBlock) - pBlock._Shift)
then Result := STATUS_SUCCESS
else Result := STATUS_INVALID_ADDRESS;
end;
procedure _LMem_SetMaxLength(const pBlock: PMEM_BLOCK; const MaximumLength: LongWord); inline;
begin
with pBlock^ do
begin
MaxLength := MaximumLength;
if Length > MaxLength then Length := MaxLength;
end;
end;
function _LMem_InitMemBlock(const pTmp: Pointer; const aMaxLength: LongWord; const HeaderSize: LongWord = 0): PMEM_BLOCK;
var
NewShift: LongWord;
begin
NewShift := OFFSET_TO_DEFINE(pTmp);
Result := PMEM_BLOCK(LongWord(pTmp) + NewShift);
with Result^ do
begin
Length := 0;
MaxLength := aMaxLength;
_Shift := NewShift;
_HeaderSize := HeaderSize;
_RealPtr := pTmp;
end;
end;
function __Call_ReallocMem(const P: Pointer; const AlignedSize: LongWord): Pointer;
begin
{$IFDEF SH_MEM_MNG}
Result := __MemMgrEx.ReallocMem(P, AlignedSize);
{$ELSE}
Result := P;
ReallocMem(Result, AlignedSize);
{$ENDIF}
end;
function _Sh_ReallocMem(var P; const AlignedSize: LongWord): NTSTATUS;
var
pTmp: Pointer;
begin
pTmp := __Call_ReallocMem(Pointer(P), AlignedSize);
if pTmp <> nil then
if pTmp <> Pointer(P) then
begin
Result := STATUS_SUCCESS;
Pointer(P) := pTmp;
end
else Result := STATUS_SH_SUCCESS
else Result := STATUS_INSUFFICIENT_RESOURCES;
end;
function __LMem_ReallocMem(var P: Pointer; const AlignedSize: LongWord): NTSTATUS;
begin
Result := _Sh_ReallocMem(P, __MEM_SIZE_INC + AlignedSize);
end;
function _LMem_ReallocMemCopy(var pBlock: PMEM_BLOCK; const AlignedSize: LongWord): NTSTATUS;
var
pTmp: Pointer;
NewShift, OldShift: LongWord;
pOldBlock: PMEM_BLOCK;
begin
Result := _LMem_GetRealPtr(pBlock, pTmp);
if Result = STATUS_SUCCESS then
begin
OldShift := pBlock._Shift;
Result := __LMem_ReallocMem(pTmp, AlignedSize + pBlock._HeaderSize);
if Result = STATUS_SUCCESS then
begin
NewShift := OFFSET_TO_DEFINE(pTmp);
pBlock := PMEM_BLOCK(LongWord(pTmp) + NewShift);
if OldShift <> NewShift then // <-- Вот тут и копируем. Т.е. при изменении самого указателя и смещения
begin
pOldBlock := Pointer(LongWord(pTmp) + OldShift);
with pOldBlock^ do
if MaxLength < AlignedSize
then Move(pOldBlock^, pBlock^, MaxLength + SizeOf(_MEM_BLOCK) + _HeaderSize)
else Move(pOldBlock^, pBlock^, AlignedSize + SizeOf(_MEM_BLOCK) + _HeaderSize);
pBlock._Shift := NewShift;
end;
pBlock._RealPtr := pTmp;
_LMem_SetMaxLength(pBlock, AlignedSize);
end
else
if Result = STATUS_SH_SUCCESS then _LMem_SetMaxLength(pBlock, AlignedSize);
end;
end;
function _LMem_ReallocMemOnly(var pBlock: PMEM_BLOCK; const AlignedSize: LongWord): NTSTATUS;
var
pTmp: Pointer;
begin
Result := _LMem_GetRealPtr(pBlock, pTmp);
if Result = STATUS_SUCCESS then
begin
Result := __LMem_ReallocMem(pTmp, AlignedSize);
if Result = STATUS_SUCCESS then pBlock := _LMem_InitMemBlock(pTmp, AlignedSize) else
if Result = STATUS_SH_SUCCESS then _LMem_SetMaxLength(pBlock, AlignedSize);
end;
end;
function LMem_ReallocMem_(var pBlock: PMEM_BLOCK; const AlignedSize: LongWord): NTSTATUS;
begin // May be copy Length only ?
if ((pBlock.MaxLength > 0) and (AlignedSize > 0)) or (pBlock._HeaderSize > 0)
then Result := _LMem_ReallocMemCopy(pBlock, AlignedSize)
else Result := _LMem_ReallocMemOnly(pBlock, AlignedSize);
end;
Riply (2008-05-09 00:51:00) [21]> [3] Германн © (09.05.08 00:36)
> Похоже на замену грибов, травкой :)
Это почему это ? :)
IMHO, лучше заранее подготовиться, чем потом перелопачивать кучу кода,
когда выясниться, что кто-то не желает работать с твоей структурой из-за ее "невыровненности" :)
Riply (2008-05-09 00:48:00) [22]> [2] guav © (09.05.08 00:19)
>> начинать новую надо
> А мож ну его ?
Эта... "Как прикажешь тебя понимать, Саид ?" :)
> Как можно избавиться от этого,
> Думаю, ты можешь копировать один раз, заменив вызов ReallocMem на AllocMem + FreeMem,
> но тогда копирование будет в любом случае.
Рассматривала этот вариант: IMHO, для большинства случаев он "непродуктивен".
Опыты показали, что ReallocMem не так уж и часто перемещает память.
(Это сильно зависит от исходного и нового размеров и еще от фазы луны :)
А мне после нее заново перемещать потребуется, только если у поинтеров
оказались разные оффсеты до границы выравнивания.
> PS: "В стандарте С функция realloc пользуется дурной славой плохо спроектированной функции.
> Она делает одновременно слишком много дел: выделяет память, если ей передано значение NULL,
> освобождает её если передан нулевой размер, перераспределяет её на том же месте,
> если это возможно или перемещает её, если таковое перераспределение невозможно.
Это одна из причин почему я рассматриваю в __Call_ReallocMem
вариант использования MemoryManagerEx.ReallocMem
Если он конечно не череват всякими "непредсказуемостями" :)
> PPS:
Да Вы, оказывается, еще и искуситель :)
Германн (2008-05-09 00:36:00) [23]
> Riply © (08.05.08 21:14)
>
> Здравствуйте !
> Учитывая горький опыт предыдущей программистской жизни,
> решила, что начинать новую надо с модуля, выравнивающего
> память :)
Похоже на замену грибов, травкой :)
guav (2008-05-09 00:19:00) [24]> [0] Riply © (08.05.08 21:14)
> начинать новую надо
А мож ну его ?
> Как можно избавиться от этого,
Думаю, ты можешь копировать один раз, заменив вызов ReallocMem на AllocMem + FreeMem, но тогда копирование будет в любом случае.
PS: "В стандарте С функция realloc пользуется дурной славой плохо спроектированной функции. Она делает одновременно слишком много дел: выделяет память, если ей передано значение NULL, освобождает её если передан нулевой размер, перераспределяет её на том же месте, если это возможно или перемещает её, если таковое перераспределение невозможно. Данная функция обычно рассматривается всеми как пример недальновидного, ошибочного проектирования"
PPS:
__declspec( align(1024) ) struct Whatever
{
char a;
} w1;
int _tmain(int argc, _TCHAR* argv[])
{
struct Whatever w2;
void *p = _aligned_malloc(100, 1024);
printf("Aligned global %p\n", &w1);
printf("Aligned local %p\n", &w2);
printf("Aligned heap %p\n", p);
p = _aligned_realloc(p, 10000, 16384);
printf("Realigned heap %p\n", p);
_aligned_free(p);
return 0;
}
/* output is
Aligned global 00417400
Aligned local 0012F400
Aligned heap 00358400
Realigned heap 0035C000
*/
Loginov Dmitry (2008-05-08 21:31:00) [25]Бог моЙ! Надеюсь, это менее опасно, чем с NTFS :))
> начинать новую надо с модуля, выравнивающего память
а еще память можно освежать и укреплять :)