Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.04.20;
Скачать: CL | DM;

Вниз

Работа с памятью. ( Выравнивание )   Найти похожие ветки 

 
Riply ©   (2008-03-20 21:50) [0]

Здравствуйте !
Допустим у нас есть очень привиредливая процедура:

procedure Fastidious(pAligned: Pointer);

Вот подавай ей на входе pAligned выровненный по границе SizeOf(Int64)
иначе она и работать откажется.
И пытаемся мы ее вызвать тремя способами:

procedure TestCallFastidious(pNotAligned: Pointer);
var
//......
MyRecord: TMySuperPuperRecord;
pTmp: Pointer;
begin
//.......  1-й вызов
Fastidious(@MyRecord);
//.......  2-й вызов
pTmp := GetMemory(MemSize);
Fastidious(pTmp);
//.......  3-й вызов
Fastidious(pNotAligned);
end;


Как лучше выкрутиться ?
Для 1-ого и 3-го приходит в голову только
Заводить доп. переменную Pointer и мучаться с перевыделением и копированием.
Со вторым вызовом тоже не понятно.
Мне не удалось получить pTmp не выровненный по 8-байтной границе
и не удалось нигде найти утверждения, что он будет выровнен.
Как с ним обстоят дела ?

P.S.
Пользоваться чем-то вроде VirtualAlloc не хотелось бы.


 
Игорь Шевченко ©   (2008-03-20 22:12) [1]


> Вот подавай ей на входе pAligned выровненный по границе
> SizeOf(Int64)
> иначе она и работать откажется.
> И пытаемся мы ее вызвать тремя способами:


var
 Filler: Int64;
 YouParameterData: TFoo;


 
palva ©   (2008-03-20 22:21) [2]

Выделить на 8 байтов больше чем надо, а потом выровнять.


 
Игорь Шевченко ©   (2008-03-20 22:25) [3]

3-ий способ - ругаться, если приходит извне. Как собственно NT ругается


 
Игорь Шевченко ©   (2008-03-20 22:26) [4]


> Со вторым вызовом тоже не понятно.


Стандартный GetMem вроде выделяет выровненную память, если мне память не изменяет.


 
palva ©   (2008-03-20 22:29) [5]


> Мне не удалось получить pTmp не выровненный по 8-байтной
> границе

Господь с вами!

{$APPTYPE CONSOLE}
var
 i1: Pointer;
 i2: Pointer;
 i3: Pointer;
begin
 i1 := GetMemory(4);
 i2 := GetMemory(4);
 i3 := GetMemory(4);
 WriteLn(Integer(i1)); // 8585220
 WriteLn(Integer(i2)); // 8585232
 WriteLn(Integer(i3)); // 8585244
end.


 
Riply ©   (2008-03-20 22:40) [6]

> [3] Игорь Шевченко ©   (20.03.08 22:25)
> 3-ий способ - ругаться, если приходит извне. Как собственно NT ругается

STATUS_INVALID_PARAMETR

Но, Игорь, это не совсем точно :)
Дело в том, что проверка производилась под Win64 (не мною).
Это самый вероятный вариант, где я спотыкаюсь.
Я излазила MSDN, и единственное объяснение, которое нашла это
данная ошибка при вызове NtQueryInformationFile(...FileStreamInformation...).
Проверить не могу так как у меня нет доступа к Win64


 
Riply ©   (2008-03-20 22:42) [7]

> [5] palva ©   (20.03.08 22:29)
> Господь с вами!

Спасибо. Значит я плохо пыталась :)


 
Riply ©   (2008-03-20 22:51) [8]

> [1] Игорь Шевченко ©   (20.03.08 22:12)

> var
>  Filler: Int64;
>  YouParameterData: TFoo;

А почему, при таком объявлении, можно быть уверенной, что YouParameterData выровнена ?


 
tesseract ©   (2008-03-20 22:55) [9]


> Стандартный GetMem вроде выделяет выровненную память, если
> мне память не изменяет.


Win64 - выделяет выровненную по 4,  если не Define-нить.


> Дело в том, что проверка производилась под Win64 (не мною).


Он линейкой проверял ?  И именно  WoW обошёл ?


 
palva ©   (2008-03-20 22:57) [10]


> А почему, при таком объявлении, можно быть уверенной, что
> YouParameterData выровнена ?
>

Нельзя быть уверенной. Вы больше пробуйте, пробуйте...


 
Игорь Шевченко ©   (2008-03-20 23:00) [11]

Riply ©   (20.03.08 22:51) [8]

Потому что компилятор размещает данные в порядке объявления.


 
Riply ©   (2008-03-20 23:00) [12]

>  [10] palva ©   (20.03.08 22:57)
> Нельзя быть уверенной. Вы больше пробуйте, пробуйте...

Допустим, я 10 000 раз получила результат, что выровненно.
Но ведь это ничего не докажет. (Правда один отрицательный докажет :)
Предлагается пробовать до первого отрицательного ? :)


 
Riply ©   (2008-03-20 23:02) [13]

> [11] Игорь Шевченко ©   (20.03.08 23:00)
> Потому что компилятор размещает данные в порядке объявления.

Тогда, почему первое объявление (Filler: Int64;) выровнено ?


 
tesseract ©   (2008-03-20 23:05) [14]


> Допустим, я 10 000 раз получила результат, что выровненно.


Win64 - это в принципе 2 среды. Одна 32 бита, вторая 64. И я не уверен что WoW пропустит вызов внутреннего API, через эмулятор. У меня на Win64 вирусы опознаються по ошибкам записи в память.


 
palva ©   (2008-03-20 23:09) [15]


> Предлагается пробовать до первого отрицательного ? :)

Ну значит мне так везет. Я всегда был неудачником :(
{$APPTYPE CONSOLE}
var
Filler: Int64;
YouParameterData: Int64;
begin
 WriteLn(Integer(@Filler));           //    4216412
 WriteLn(Integer(@YouParameterData)); //    4216420
end.


 
tesseract ©   (2008-03-20 23:11) [16]


> palva ©   (20.03.08 23:09) [15]


Выравнивание 8 байт. Или я что-то не понял ?


 
Riply ©   (2008-03-20 23:12) [17]

>  [15] palva ©   (20.03.08 23:09)
> Ну значит мне так везет. Я всегда был неудачником :(

Вот досада :) Как же тогда исхитриться и объявить "ровно" ?


 
guav ©   (2008-03-20 23:20) [18]

> var
> Filler: Int64;
> YouParameterData: TFoo;

Если способ с Filler работает, предлагаю
var
 R: record
   Filler: Int64;
   YouParameterData: TFoo;
 end;
Компилятор может поместить отдельную Filler в пару регистров или выкинуть его совсем, а в случае записи он вряд ли догадается соптимизировать.

__declspec(align(8)) в Deplhi насколько я знаю нет, есть $ALIGN но это немного не то.

Насчёт второго случая я за [2] или VirtualAlloc.
Насчёт третьего - можно проверить выравнивание, если неправильное, выделить новый буфер, из которого/в который копировать, и свести ко второму.


 
palva ©   (2008-03-20 23:27) [19]


> Вот досада :) Как же тогда исхитриться и объявить "ровно"
> ?

Если palva ©   (20.03.08 22:21) [2] не подойдет.
то можно исхитриться с промежуточной процедурой на ассемблере.
Где нужно объявить данные? Глобально или в процедуре?


 
tesseract ©   (2008-03-20 23:31) [20]


> Компилятор может поместить отдельную Filler в пару регистров
> или выкинуть его совсем, а в случае записи он вряд ли догадается
> соптимизировать.


навряд-ли в регистры - он их 32-х считает (Delphi) а int64, как факт, ещё с первых пней идёт.


 
palva ©   (2008-03-20 23:35) [21]

Процедура на ассемблере (без параметров) должна проверить текущий адрес стека и в том случае, если он делится/не делится на 8, увеличить стек на 4 после чего сразу же вызвать ту процедуру в которой выделяется память. После возврата из процедуры стек нужно вернуть назад на 4.
Встроеный ассемблер здесь, по-моему, не подойдет. Требуются эксперименты.
Возникнут осложнения, если существенно требуется передача параметров.


 
palva ©   (2008-03-20 23:42) [22]

Вообще-то без ассемблера можно обойтись. Только нужны подробности задачи.


 
guav ©   (2008-03-20 23:54) [23]

> [20] tesseract ©   (20.03.08 23:31)
> навряд-ли в регистры - он их 32-х считает (Delphi) а int64,
> как факт, ещё с первых пней идёт.

Может EDX:EAX, как используется для результата функции, я же написал про пару регистров. В любом случае, лучше делать меньше подобных предположений о коде сгенерированом компилятором.


 
palva ©   (2008-03-21 00:07) [24]

Вот такой набросок

{$APPTYPE CONSOLE}

procedure memalign; // Это процедура, в которой выделяются данные
var
YouParameterData: Int64;
begin
 WriteLn(Integer(@YouParameterData));
 // Fastidious(@YouParameterData)
end;

procedure mediator;
begin
 memalign;
end;

procedure caller;
var itest: Integer;
begin
 if Integer(@itest) and 7 = 0 then
   mediator   // Эти две строки возможно
 else
   memalign;  // надо будет поменять местами при настройке
end;

begin
 caller;
end.


 
guav ©   (2008-03-21 00:12) [25]

> [24] palva ©   (21.03.08 00:07)

Ага, и после этого "не дышать" на код memalign дабы не изменить количество и порядок  данных в стеке.
Не знаю как Riply, но мне этот цирк не нравится, имхо, если нет средства выравнивания на стеке, значит надо выделять динамически.


 
palva ©   (2008-03-21 00:21) [26]


> Не знаю как Riply, но мне этот цирк не нравится,

Мне тоже

> если нет средства выравнивания на стеке, значит надо выделять
> динамически.

То есть вернемся к [2]


 
Riply ©   (2008-03-21 00:46) [27]

>  [25] guav ©   (21.03.08 00:12)
> Не знаю как Riply, но мне этот цирк не нравится

Riply тоже не любит цирк :)

>  [26] palva ©   (21.03.08 00:21)
> То есть вернемся к [2]

Уже пишется маленькая структура, которая будет этим заниматься :)


 
palva ©   (2008-03-21 01:01) [28]

Переходите на си (Borland 5.5)

#pragma pack(4)
#include <stdio.h>
void main() {
 struct {
   int a;
   __int64 d;
   int n;
 } s;
 printf("%d %d\n", &s.d, &s.n);  // 1245056 1245064
}


 
Riply ©   (2008-03-21 01:29) [29]

> [28] palva ©   (21.03.08 01:01)
> Переходите на си (Borland 5.5)

Так. Все сговорились :) В один голос предлагают переходить на С.
Знаю, что надо его осваивать. Да вот все духу не хватает :(


 
Германн ©   (2008-03-21 01:46) [30]


> Riply ©   (21.03.08 01:29) [29]
>
> > [28] palva ©   (21.03.08 01:01)
> > Переходите на си (Borland 5.5)
>
> Так. Все сговорились :) В один голос предлагают переходить
> на С.
> Знаю, что надо его осваивать. Да вот все духу не хватает
> :(
>

Не, ну освоить СИ в основах не так сложно, как кажется. Скажу больше. Это гораздо проще, чем то, что ты изучаешь своим методом ННТ. Токмо вот возникает другой вопрос после прочтения:

> Допустим у нас есть очень привиредливая процедура:
>
> procedure Fastidious(pAligned: Pointer);
>
> Вот подавай ей на входе pAligned выровненный по границе
> SizeOf(Int64)
> иначе она и работать откажется.

А вдруг после освоения СИ ты приведешь пример другой "привиредливой" функции, но уже на СИ? Что тогда?


 
han_malign ©   (2008-03-21 10:29) [31]


> > [24] palva ©   (21.03.08 00:07)
>
> Ага, и после этого "не дышать" на код memalign дабы не изменить
> количество и порядок  данных в стеке.

угу, а также на $D+/- и $W+/-...

var
    _buf: array[0..sizeof(TMyRec)+cMyAlign];
    pbuf: PMyRec;
    offs: LongWord;
begin
    pbuf:= PMyRec(_buf) ;
    offs:= LongWord(pbuf) mod cMyAlign;//для 32-бит, если появится 64-битный Delphi - int64 и воможно IFDEF
    if( offs > 0)then inc(PChar(pbuf), cMyAlign - offs);

- с небуферизированным чтением файла у меня такое прокатывало...


 
han_malign ©   (2008-03-21 10:37) [32]

PMyRec(@_buf) - собаку забыл.

лишний байт в _buf, но мы люди не жадные :)...

для 64-бит(если когда появится) - если выравнивание на степень 2-ки - LongWord всегда прокатит, а mod сAlign можно заменить на and (cAlign - 1)(cAlign - есесьно не 0)...


 
Sha ©   (2008-03-21 10:46) [33]

pTmp := GetMemory(PMyRecordSize+4);
pMyRecord:=pointer((integer(pTmp)+4) and -8);
Fastidious(pMyRecord);


по идее, должно работать для любого Delphi MM


 
Riply ©   (2008-03-21 13:01) [34]

> [31] han_malign ©   (21.03.08 10:29)
> - с небуферизированным чтением файла у меня такое прокатывало...

Ну вот и со "статическим" объявлением разобрались. :)

> [33] Sha ©   (21.03.08 10:46)
> pointer((integer(pTmp)+4) and -8);

Элегантно, ничего не скажешь.
А я то..., всякие условия "если это по модулю, то..." проверяю :)

Спасибо всем большое !


 
Riply ©   (2008-03-21 13:28) [35]

> [33] Sha ©   (21.03.08 10:46)
> pMyRecord:=pointer((integer(pTmp)+4) and -8);

Проглядела: например при integer(pTmp) = 1, получаем указатель не на нашу память :)
Вроде так получается:
pMyRecord:= pointer((DWord(pTmp) + 7) and -8);


 
Sapersky   (2008-03-21 13:57) [36]

Мне не удалось получить pTmp не выровненный по 8-байтной границе

Зависит от версии Delphi, точнее, менеджера памяти. FastMM в BDS2006 по умолчанию выравнивает на 8:
http://dn.codegear.com/article/33416
Стандартный менеджер более ранних Delphi - на 4.

По поводу выравнивания переменных была информация здесь:
http://dennishomepage.gugs-cats.dk/CodingForSpeedInDelphi.doc


 
Sha ©   (2008-03-21 14:31) [37]

> Riply ©   (21.03.08 13:28) [35]

Sapersky   (21.03.08 13:57) [36]  уже  ответил :)

для стандартного менеджера памяти ранних версий Delphi
см. также использование константы cAlign в в Source\RTL\Sys\getmem.inc

нечто похожее есть и в FastMM, который, как и любой менеджер памяти,
обязан быть совместимым со стандартным.
Т.е. ранее написанные программы должны сохранять работособность с любымм MM,
и, значит, любая выделяемая память обязана быть выровнена по крайней мере на 4.


 
palva ©   (2008-03-21 22:59) [38]

Язык си я зря предложил. Решить проблему он не поможет.


 
Riply ©   (2008-03-21 23:45) [39]

> [38] palva ©   (21.03.08 22:59)
> Язык си я зря предложил. Решить проблему он не поможет.

Меня тут мучает другое.
Учитывая [36] Sapersky и [37] Sha © , подумала:
А может под BDS 2006 будет работать [1] Игорь Шевченко © ?
Опять таки, четких утверждений мне найти не удалось,
но не удалось, также, и получить "невыровненную" YouParameterData: TFoo;
если ее размер превышает SizeOf(Int64).
Конечно, можно плюнуть на это дело, и использовать вариант типа [31] han_malign,
но меня уже "зацепило"  :)


 
Leonid Troyanovsky ©   (2008-03-22 15:06) [40]


> Riply ©   (21.03.08 23:45) [39]

> но меня уже "зацепило"  :)

Все же не очень понятно, чем не угодила VirtualAlloc.

А по поводу дельфийского ММ хелп моей TD обещал лишь

Memory manager blocks are always rounded upward to a 4-byte boundary, and
always include a 4-byte header in which the size of the block and other status
bits are stored. This means that memory manager blocks are always double-
word-aligned, which guarantees optimal CPU performance when addressing

Т.е., если уж так хочется 8 байт, то проси Size+8 байт, и в случае если полученный адрес не кратен 8 - сдвинь указатель на 4.
Ну, и {$A8}, IMHO, здесь не повредит.

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

--
Regards, LVT.



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

Текущий архив: 2008.04.20;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.018 c
4-1178810111
йцукенг
2007-05-10 19:15
2008.04.20
Как получить handle элемента управления окна?


15-1204887995
Igor_
2008-03-07 14:06
2008.04.20
define для определения версии C++ Builder


2-1206733636
Wold
2008-03-28 22:47
2008.04.20
TMainMenu + OnDrawItem


15-1204924214
Kostafey
2008-03-08 00:10
2008.04.20
С днем рождения ! 8 марта


2-1206488896
tim
2008-03-26 02:48
2008.04.20
потоки