Форум: "Начинающим";
Текущий архив: 2008.04.20;
Скачать: [xml.tar.bz2];
ВнизРабота с памятью. ( Выравнивание ) Найти похожие ветки
← →
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;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.036 c