Форум: "Прочее";
Текущий архив: 2008.07.27;
Скачать: [xml.tar.bz2];
ВнизКоварный inline :) Найти похожие ветки
← →
LightRipple © (2008-06-09 13:40) [0]Здравствуйте !
Допустим есть у нас вот такой код:type
PMEM_BLOCK = ^MEM_BLOCK;
MEM_BLOCK = packed record
Length: ULONG;
_MaxLength: ULONG;
pBuffer: Pointer;
_DiffPtr: ULONG;
end;
function BlockMem_GetMem(const pMemBlock: PMEM_BLOCK; const AllocSize: ULONG): NTSTATUS;
begin
pMemBlock.pBuffer := GetMemory(AllocSize);
if pMemBlock.pBuffer <> nil then
begin
pMemBlock.Length := 0;
pMemBlock._MaxLength := AllocSize;
Result := STATUS_SUCCESS;
end
else Result := STATUS_INSUFFICIENT_RESOURCES;
end;
function BlockMem_SetUsedLength(const pMemBlock: PMEM_BLOCK; const UsedLength: ULONG): NTSTATUS;{$IFNDEF INLINE_TEST}inline;{$ENDIF}
begin
if UsedLength <= pMemBlock._MaxLength then
begin
pMemBlock.Length := UsedLength;
Result := STATUS_SUCCESS;
end
else Result := STATUS_NAME_TOO_LONG;
end;
function BlockMem_MemByOffset(const pMemBlock: PMEM_BLOCK; const Offset: ULONG): Pointer;{$IFNDEF INLINE_TEST}inline;{$ENDIF}
begin
Result := Pointer(ULONG(pMemBlock.pBuffer) + Offset);
end;
procedure _BlockMem_MoveByOffset(const pMemBlock: PMEM_BLOCK; const Buffer; const Offset, BytesWrite: ULONG);{$IFNDEF INLINE_TEST}inline;{$ENDIF}
begin
if BytesWrite > 0 then Move(Buffer, BlockMem_MemByOffset(pMemBlock, Offset)^, BytesWrite);
end;
function BlockMem_MoveByOffset(const pMemBlock: PMEM_BLOCK; const Buffer; const Offset, BytesWrite: ULONG): NTSTATUS;{$IFNDEF INLINE_TEST}inline;{$ENDIF}
begin
Result := BlockMem_SetUsedLength(pMemBlock, Offset + BytesWrite);
if Result = STATUS_SUCCESS then _BlockMem_MoveByOffset(pMemBlock, Buffer, Offset, BytesWrite);
end;
function BlockMem_MoveAppend(const pMemBlock: PMEM_BLOCK; const Buffer; const BytesWrite: ULONG): NTSTATUS;
begin
Result := BlockMem_MoveByOffset(pMemBlock, Buffer, pMemBlock.Length, BytesWrite);
end;
Какое сообщение мы увидим при следующем вызове ?procedure TMainForm.SpeedButton1Click(Sender: TObject);
var
MemBlock: MEM_BLOCK;
wBuf: WideString;
RetStatus: NTSTATUS;
begin
wBuf := "Hello Dolly !";
RetStatus := BlockMem_GetMem(@MemBlock, 4096);
if RetStatus = STATUS_SUCCESS then
try
FillChar(MemBlock.pBuffer^, 4096, 0);
RetStatus := BlockMem_MoveAppend(@MemBlock, Pointer(wBuf)^, Length(wBuf) * SizeOf(WideChar));
if NT_SUCCESS(RetStatus) then ShowMessage(PWideChar(MemBlock.pBuffer));
finally
FreeMem(MemBlock.pBuffer);
end;
end;
"Hello Dolly !" ?
А вот и не угадали - пустое. Чтобы найти приветствие для Dolly, надо либо вызвать
ShowMessage(PWideChar(DWord(MemBlock.pBuffer) + DWord(Length(wBuf)) * SizeOf(WideChar))),
либо прописать {$DEFINE INLINE_TEST} перед нашим кодом :)
← →
Alien1769 © (2008-06-09 14:55) [1]И шо это было ?
Вопрос для мастеров или замечание для новичков :)
← →
версия для печати (2008-06-09 14:56) [2]Удалено модератором
← →
guav © (2008-06-09 15:07) [3]> [1] Alien1769 © (09.06.08 14:55)
> И шо это было ?
Думаю скрытый вопрос "что сломано - код или компилятор?"
← →
LightRipple © (2008-06-09 15:38) [4]> [3] guav © (09.06.08 15:07)
> Думаю скрытый вопрос "что сломано - код или компилятор?"
Не, [1] Alien1769 © был ближе к истине - "замечание для новичков",
что inline функции могут себя вести не "так, как глаза видят" :)
P.S.
Пришлось потратить время на поиск ошибки,
который усугублялся тем, что при пошаговой отладке не заходим в inline.
← →
guav © (2008-06-09 15:40) [5]> [4] LightRipple © (09.06.08 15:38)
Т.е. ты гарантируешь что сломан именно компилятор ?
← →
LightRipple © (2008-06-09 15:47) [6]> [5] guav © (09.06.08 15:40)
> Т.е. ты гарантируешь что сломан именно компилятор ?
Саш, где я такое сказала ?
Код корректен, если мы не объявляем inline.
Он же становится некорректным в другом случае.
(для inline версии в коде "появляется" самая настоящая ошибка)
← →
LightRipple © (2008-06-09 15:50) [7]> [6] LightRipple © (09.06.08 15:47)
Иными словами: если объявлен inline, то в коде есть ошибка.
← →
guav © (2008-06-09 15:53) [8]Я думаю, что в данном случае inline ни на что не должно влиять.
т.е. одно из двух:
1. Ошибка типа undefined behaviour, вот и поведение разное
2. Компилятор ошибается, вот и поведение разное.
← →
LightRipple © (2008-06-09 15:59) [9]> [8] guav © (09.06.08 15:53)
> Я думаю, что в данном случае inline ни на что не должно влиять.
Влияет, еще и как :)
Перепиши функцию так:function BlockMem_MoveByOffset(const pMemBlock: PMEM_BLOCK; const Buffer;
const Offset, BytesWrite: ULONG): NTSTATUS;{$IFNDEF INLINE_TEST}inline;{$ENDIF}
var
Tmp: ULONG;
begin
Tmp := Offset;
Result := BlockMem_SetUsedLength(pMemBlock, Tmp + BytesWrite);
if Result = STATUS_SUCCESS then _BlockMem_MoveByOffset(pMemBlock, Buffer, Tmp, BytesWrite);
end;
и ошибка исчезнет :) Она возникает именно из-за inline директивы.
← →
guav © (2008-06-09 16:02) [10]> [9] LightRipple © (09.06.08 15:59)
Значит баг компилятора. Такого не должно быть.
← →
LightRipple © (2008-06-09 16:08) [11]> [10] guav © (09.06.08 16:02)
> Значит баг компилятора. Такого не должно быть.
Я сначала тоже не понимала.
Но все правильно.
В случае inline мы не "вызываем" ф-ию с параметрами, а "подставляем ее код" вместо вызова.
Вот и получается, что в строчках:Result := BlockMem_SetUsedLength(pMemBlock, Offset + BytesWrite);
if Result = STATUS_SUCCESS then _BlockMem_MoveByOffset(pMemBlock, Buffer, Offset, BytesWrite);
Offset становится разным, т.к. подставляется на самом деле не Offset, а pMemBlock.Length.
А он уже успел измениться :)
← →
clickmaker © (2008-06-09 16:16) [12]ну да, это как с define max(a, b) ((a) > (b) ? (a) : (b))
c = max(a++, b)
примерно те же грабельки
← →
guav © (2008-06-09 16:17) [13]> Offset становится разным
Так не должно такого быть.
Может в delphi какой-то свой смысл инлайна ?
Хотя вряд ли, думаю должно быть как в С++ - вычисление параметров только один раз.
Хочешь сказать что тут будут 3 разных значения ?function x(): Integer;
begin
Random(100);
end;
procedure y(i: Integer); inline;
begin
WriteLn(i);
WriteLn(i);
WriteLn(i);
end;
begin
y(x());
end.
← →
guav © (2008-06-09 16:19) [14]> [12] clickmaker © (09.06.08 16:16)
Ну так это препроцессор, тут так надо.
Инлайновый max:template <typename T>
inline T max(T a, T b)
{
return (((a) > (b)) ? (a) : (b));
}
не содержит этих граблей.
← →
LightRipple © (2008-06-09 16:26) [15]> [13] guav © (09.06.08 16:17)
> Так не должно такого быть.
Но так есть. Скомпилируй мой код - увидишь своими глазами :)
> Хотя вряд ли, думаю должно быть как в С++ - вычисление параметров только один раз.
Параметры и вычисляются один раз для вызыва функции,
а в случае inline этого вызова вроде как и нет.
← →
guav © (2008-06-09 16:33) [16]
> [15] LightRipple © (09.06.08 16:26)
> а в случае inline этого вызова вроде как и нет.
Вызов есть. Не важно заинлайнен он или нет.
Я считаю, что наблюдаемое поведение противоречит здравому смыслу. В С/С++ точно не так.
> Скомпилируй мой код - увидишь своими глазами
> :)
Верю что баг возможен.
Но ты попробуй более простой случай - например мой код (поправь там Result := Random(100);).
Удивлюсь если i действительно вычислится 3 раза.
← →
LightRipple © (2008-06-09 16:40) [17]> [16] guav © (09.06.08 16:33)
> Верю что баг возможен.
Ой не надо мне верить :)
Я не забыла историю с OPEN_IF. Если бы ты там мне поверил, то мы бы и до истины не докопались бы :)
> Но ты попробуй более простой случай
Хорошо. Попробую упростить код с сохранением его свойств :)
← →
LightRipple © (2008-06-09 16:57) [18]> [17] LightRipple © (09.06.08 16:40)
> Хорошо. Попробую упростить код с сохранением его свойств :)type
PMEM_BLOCK = ^MEM_BLOCK;
MEM_BLOCK = packed record
Length: ULONG;
_MaxLength: ULONG;
end;
function BlockMem_SetUsedLength(const pMemBlock: PMEM_BLOCK; const UsedLength: ULONG): NTSTATUS; inline;
begin
if UsedLength <= pMemBlock._MaxLength then
begin
pMemBlock.Length := UsedLength;
Result := STATUS_SUCCESS;
end
else Result := STATUS_NAME_TOO_LONG;
end;
function BlockMem_MoveByOffset(const pMemBlock: PMEM_BLOCK; const Offset, BytesWrite: ULONG): NTSTATUS; inline;
var
Tmp: ULONG;
begin
Tmp := Offset;
Result := BlockMem_SetUsedLength(pMemBlock, Offset + BytesWrite);
if Result = STATUS_SUCCESS then Assert(Tmp = Offset);
end;
function BlockMem_MoveAppend(const pMemBlock: PMEM_BLOCK; const BytesWrite: ULONG): NTSTATUS;
begin
Result := BlockMem_MoveByOffset(pMemBlock, pMemBlock.Length, BytesWrite);
end;
procedure TMainForm.SpeedButton1Click(Sender: TObject);
var
MemBlock: MEM_BLOCK;
begin
MemBlock._MaxLength := 4096;
BlockMem_MoveAppend(@MemBlock, 10);
end;
Вот :)
← →
guav © (2008-06-09 16:57) [19]А мой код что выдаёт ?
← →
LightRipple © (2008-06-09 17:00) [20]> [19] guav © (09.06.08 16:57)
> А мой код что выдаёт ?
Твой еще не успела. Сейчас попробую, только этот еще подправлю.
← →
LightRipple © (2008-06-09 17:08) [21]Вроде больше выбрасывать нечего:
type
PMEM_BLOCK = ^MEM_BLOCK;
MEM_BLOCK = packed record
Length: ULONG;
end;
function BlockMem_SetUsedLength(const pMemBlock: PMEM_BLOCK; const UsedLength: ULONG): NTSTATUS; inline;
begin
pMemBlock.Length := UsedLength;
Result := STATUS_SUCCESS;
end;
function BlockMem_MoveByOffset(const pMemBlock: PMEM_BLOCK; const Offset, BytesWrite: ULONG): NTSTATUS; inline;
var
Tmp: ULONG;
begin
Tmp := Offset;
Result := BlockMem_SetUsedLength(pMemBlock, Offset + BytesWrite);
if Result = STATUS_SUCCESS then Assert(Tmp = Offset, IntToStr(Tmp) + sLineBreak + IntToStr(Offset));
end;
function BlockMem_MoveAppend(const pMemBlock: PMEM_BLOCK; const BytesWrite: ULONG): NTSTATUS;
begin
Result := BlockMem_MoveByOffset(pMemBlock, pMemBlock.Length, BytesWrite);
end;
← →
LightRipple © (2008-06-09 17:22) [22]> [19] guav © (09.06.08 16:57)
> А мой код что выдаёт ?
Твой выдает то что и ожидается.
Но он не совсем точно воспроизводит ситуацию моего кода.
← →
guav © (2008-06-09 17:33) [23]> [22] LightRipple © (09.06.08 17:22)
> Но он не совсем точно воспроизводит ситуацию моего кода.
Осталось только объяснить в чём разница, где граница и почему это не баг а фича :)
← →
LightRipple © (2008-06-09 18:56) [24]> [23] guav © (09.06.08 17:33)
> Осталось только объяснить в чём разница, где граница и почему это не баг а фича :)
С разницей, допустим, разберемся.
"не баг а фича" потому что я тысячи раз слышала о том, что глючит Delphi или Windows,
и каждый раз оказывалось, что эти утверждения не соответствовали истине.
И, пока, нет оснований полагать, что данный пример чем-то отличается от своих предшественников :)
← →
guav © (2008-06-09 22:18) [25]Вот код, который воспроизводит баг:
procedure Test1(const P: PInteger; const I: Integer); inline;
var
J: Integer;
begin
J := I;
P^ := 1;
Assert(J = I);
end;
procedure Test();
var
I: Integer;
begin
I := 0;
Test1(@I, I);
end;
А вот чуть изменённый, который не вопроизводит:procedure Test1(const P: PInteger; const I: Integer); inline;
var
J: Integer;
begin
J := I;
P^ := 1;
Assert(J = I);
end;
var
I: Integer;
procedure Test();
begin
I := 0;
Test1(@I, I);
end;
В справке про то что aliasing приводит к UB при инланинге - не нашел.
← →
LightRipple © (2008-06-09 23:43) [26]> [25] guav © (09.06.08 22:18)
> Вот код, который воспроизводит баг:
> ....
> В справке про то что aliasing приводит к UB при инланинге - не нашел.
Т.е. будем считать, что это баг, а не безграмотность автора кода ?
Я правильно поняла ?
← →
guav © (2008-06-10 00:19) [27]> [26] LightRipple © (09.06.08 23:43)
> Т.е. будем считать, что это баг, а не безграмотность автора кода ?
Считай как хочешь.
Это в С всё проверяется, т.к. и стандарт есть и компиляторов много (вот здесь часто проверяют http://www.comeaucomputing.com/tryitout/ ).
В случае Delphi имеем один компилятор, не всегда надёжный и не все конструкции языка хорошо документированы. Поэтому трудно сказать.
Но я считаю это багом.
← →
guav © (2008-06-10 00:34) [28]Зачем вообще применять директиву inline, что ты от неё ожидаешь ?
← →
LightRipple © (2008-06-10 00:42) [29]> [28] guav © (10.06.08 00:34)
> Зачем вообще применять директиву inline, что ты от неё ожидаешь ?
В данном случае, я просто смотрела как она влияет на быстродействие и "разбухание кода".
Часто встречаю ее в чужом коде, видела и в генофонде (вроде Math, не помню).
← →
guav © (2008-06-10 01:27) [30]> [29] LightRipple © (10.06.08 00:42)
Вот именно, мелочи вроде размера кода и скорости его выполнения. Но не другой смысл вызова функции.
> как она влияет на быстродействие
Замедляет (подобрать количество Inc(M); и Test1(a); чтобы замедляло :-)procedure Test1(var M: Integer); inline;
begin
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
Inc(M); Inc(M); Inc(M); Inc(M); Inc(M);
end;
procedure Test();
var a: Integer;
begin
Test1(a);
end;
var T: DWORD; I: Integer;
begin
T := GetTickCount();
for I := 0 to 500000 do
Test();
T := GetTickCount() - T;
WriteLn(T);
ReadLn;
end.
> [29] LightRipple © (10.06.08 00:42)
> и "разбухание кода".
Снижаетprocedure Test1(var I: Integer); inline;
begin
I := I + 1;
end;
procedure Test();
var a,b,c,d,e: Integer;
begin
Test1(a);
Test1(b);
Test1(c);
Test1(d);
Test1(e);
end;
Два способа правильного применения инлайна: 1. ничего не инлайнить, компилятор сам заинлайнит когда это нужно, если конечно он это умеет. 2. осознанно инлайнить или запрещать инлайн в узких местах, руководствуясь результатами профилирования. А необоснованый инлайн - это "Premature optimization, the root of all evil".
← →
LightRipple © (2008-06-10 07:24) [31]> [30] guav © (10.06.08 01:27)
Спасибо :)
← →
guav © (2008-06-10 11:08) [32](на всякий случай: встраивание может привести к ускорению и к "разбуханию", но ожидать этого в каждом случае - ошибка)
← →
Игорь Шевченко © (2008-06-10 12:02) [33]
> 1. ничего не инлайнить, компилятор сам заинлайнит когда
> это нужно, если конечно он это умеет
Э...а как это компилятор Delphi умеет ?
← →
guav © (2008-06-10 12:17) [34]Delphi никак.
Я к тому что есть компиляторы которые делают встраивание без всяких директив (это встраивание совершенно не мешает, т.к. ни к каким побочным эффектам не приводит). А также к тому что раз на результат выполнения inline не должно влиять, оно скорее не нужно чем нужно.
← →
Игорь Шевченко © (2008-06-10 12:49) [35]
> Delphi никак.
речь о delphi идет вроде как. А в delphi inline выполняется согласно директиве.
> А также к тому что раз на результат выполнения inline не
> должно влиять, оно скорее не нужно чем нужно
inline нужно для увеличения быстродействия - собственно для этого оно и придумано, как и развертывание циклов
← →
guav © (2008-06-10 13:24) [36]> [35] Игорь Шевченко © (10.06.08 12:49)
> inline нужно для увеличения быстродействия
Я же не написал "не нужно". Я написал "скорее не нужно чем нужно".
Инлайн и развёртывание во всех случаях где это можно - это та самая преждевременная оптимизация.
Где-то инлайн и развёртывание будут дейтвительно полезны. Где-то - приводить к снижению быстродействия. Где-то ещё - раскроют глюки компилятора :-) . А в большинстве случаев ни приведут ни к каким ощутимым результатам. Не так ли ?
Страницы: 1 вся ветка
Форум: "Прочее";
Текущий архив: 2008.07.27;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.007 c