Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 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.248 c
15-1204784753
DrAndrey
2008-03-06 09:25
2008.04.20
Выбор ADSL модема с 4 портами LincSys vs Zyxel vs D-Link


2-1206354572
webSQLNeederr
2008-03-24 13:29
2008.04.20
Правельно ли я понял по поводу Application.Processmessages; ?


15-1204619181
@!!ex
2008-03-04 11:26
2008.04.20
Windows XP 32 + AMD 64 глюк


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


2-1206554437
ini
2008-03-26 21:00
2008.04.20
Как вскормить TStrings TIniFile-у





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский