Inline vs CopyPast и способ передачи параметров в процедуры.

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Inline vs CopyPast и способ передачи параметров в процедуры.

Сообщение iN0k » 27.07.2012 09:35:40

очень часто встречаются куски подобного кода:

Код: Выделить всё
  rMyRecord=record
    field0:..
    ...
    fieldN:..
   end;
  pMyRecord=^rMyRecord;

...

procedure F0(prm:pMyRecord);
procedure F1(prm:rMyRecord);



код F1 и F0 функционально совпадает на 100%, разница только в параметре.
Код: Выделить всё
procedure F0(prm:pMyRecord);
begin
    // что-то делаем
end;

procedure F1(prm:rMyRecord);
begin
    F0(@prm);
end;




Вопрос:
каким образом расставить расставить модификаторы inline, const, var для достижения следующих вещей:
1. сосредоточение кода в ЕДИНСТВЕННОМ месте
2. оптимальное, по скорости, исполнение кода для обоих модификаций вызова.
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение Brainenjii » 27.07.2012 11:50:23

generic?
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение iN0k » 27.07.2012 11:59:34

Brainenjii писал(а):generic?

гм ... скорее всего туплю ...

можно вариант использования?
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение hinst » 27.07.2012 12:57:30

inline надо сделать F0, где указатель передаётся
const надо поставить в обоих процедурах, так как параметры не изменяются ни там, ни там, т. что нет смысла их копировать лишний раз (они ведь копируются если const или var не стоит)
Я вообще когда пишу код ставлю const практически у 99% аргументов.
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение Brainenjii » 27.07.2012 13:46:50

а, не так вопрос понял ^_^ Плюс, оказалось что обобщения нельзя делать для процедур. В общем вышло так:
Код: Выделить всё
program Project1;

Type

{ WTF }

Generic WTF<T> = Class
  Public
    Constructor Yahoo(Const a: T);
End;

Type rRecord = Record
  Value: Integer;
End;

Type pRecord = Record
  Value: String;
End;

Type rWTF = Specialize WTF<rRecord>;
Type pWTF = Specialize WTF<pRecord>;

{ WTF }

Constructor WTF.Yahoo(Const a: T);
Begin
  WriteLn(a.Value);
  Free;
  Self := nil;
End;

Var
  ar: rRecord;
  ap: pRecord;
begin
  ar.Value := 100;
  ap.Value := 'Yahoo';
  rWTF.Yahoo(ar);
  pWTF.Yahoo(ap);

end.
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение Sergei I. Gorelkin » 27.07.2012 14:08:39

А зачем вообще нужно держать два варианта, отличающиеся только уровнем косвенности (indirection level)? Что мешает оставить, например, только вариант с указателем и вызывать foo(@rec) в тех случаях, где применялся другой вариант?
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение iN0k » 30.07.2012 09:17:57

Sergei I. Gorelkin писал(а):А зачем вообще нужно держать два варианта, отличающиеся только уровнем косвенности (indirection level)? Что мешает оставить, например, только вариант с указателем и вызывать foo(@rec) в тех случаях, где применялся другой вариант?


область применения: удобство использования библиотеки написанной таким образом.
хочется исеть возможность использовать процедуру следующим образом.

Код: Выделить всё
procedure FFF(prm:pMyRecord); overload;
begin
    // что-то делаем
end;

procedure FFF(prm:rMyRecord); overload;
begin
    FFF(@prm);
end;
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение sign » 30.07.2012 11:16:57

iN0k писал(а):хочется исеть возможность использовать процедуру следующим образом.


И что вам не даёт использовать overload?
Всё работает замечательно, вот пример. Или я что-то не понял, что вам именно нужно,
Код: Выделить всё
type
  r1 = record
    p1: Integer;
  end;
  p1 = ^r1;

procedure f1(pr1: r1);
procedure f1(pr1: p1);

implementation

{$R *.lfm}

procedure f1(pr1: r1);
begin
  pr1.p1 := 0;
end;

procedure f1(pr1: p1);
begin
  f1(pr1^)
end;                     


Добавлено спустя 1 минуту 6 секунд:
Re: Inline vs CopyPast и способ передачи параметров в процедуры.
Либо обратным ходом
Код: Выделить всё
procedure f1(pr1: r1);
begin
  f1(@pr1)
end;

procedure f1(pr1: p1);
begin
  pr1^.p1 := 0;
end;
sign
энтузиаст
 
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение iN0k » 01.08.2012 13:06:42

к сожалению в асемблере для компов я не силен (совсем слобак :oops:)
и возможно мои методы оценкок не верны (а в корне ошибочны :P)

накидав тест имею:

это "некая Офигительная Библиотека"
Код: Выделить всё
unit Unit3;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type

pMyRecord=^rMyRecord;
rMyRecord=record
    next:pMyRecord;
    F0  :integer;
    F1  :integer;
    F2  :integer;
  end;



procedure prcRP_p(const node:pMyRecord);  inline;
procedure prcRP_r(const node:rMyRecord);  inline;

procedure prcPR_r(var   node:rMyRecord);  inline;
procedure prcPR_p(const node:pMyRecord);  inline;


implementation

procedure prcRP_p(const node:pMyRecord);
begin
    with node^ do begin
      F2:=F0+F1;
    end;
end;

procedure prcRP_r(const node:rMyRecord);
begin
    prcRP_p(@node);
end;

procedure prcPR_r(var node:rMyRecord);
begin
    with node do begin
      F2:=F0+F1;
    end;
end;

procedure prcPR_p(const node:pMyRecord);
begin
   prcPR_r(node^)
end;

end.   


а также способ использования "некой Офигительной Библиотеки"
Код: Выделить всё
unit Unit2;

{$mode objfpc}{$H+}

interface

uses Unit3,
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm2 }

  TForm2 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
  private
    { private declarations }
  public
    myRecord:rMyRecord;
  end;


  pTest=^rTest;
  rTest=record
     next:pTest;
     a   :integer;
   end;



var
  Form2: TForm2;





implementation

function testGetNext_00(var node:pTest):pTest;  inline;
begin
    result:=node^.next;
end;

function testGetNext_01(const node:pTest):pTest;  inline;
begin
    result:=node^.next;
end;

function testGetNext_03(const node:rTest):pTest;  inline;
begin
    result:=node.next;
end;

{ TForm2 }

procedure TForm2.Button1Click(Sender: TObject);
var node:rTest;
    p   :pTest;
begin
    node.a:=10;
    p:=@node;
    p:=p^.next;

    node.a:=11;
    p:=@node;
    p:=testGetNext_00(p);

    node.a:=12;
    p:=@node;
    p:=testGetNext_01(p);

    node.a:=13;
    p:=@node;
    p:=testGetNext_03(node);

end;

procedure TForm2.Button2Click(Sender: TObject);
begin
    prcRP_r(myRecord);
    {
    Unit2.pas:92                      prcRP_r(myRecord);
    004234CB 89e7                     mov    %esp,%edi
    004234CD 8db074040000             lea    0x474(%eax),%esi
    004234D3 fc                       cld
    004234D4 b904000000               mov    $0x4,%ecx
    004234D9 f3a5                     rep movsl %ds:(%esi),%es:(%edi)
    004234DB 89e0                     mov    %esp,%eax
    004234DD 8b5004                   mov    0x4(%eax),%edx
    004234E0 8b4808                   mov    0x8(%eax),%ecx
    004234E3 01ca                     add    %ecx,%edx
    004234E5 89500c                   mov    %edx,0xc(%eax)
    Unit2.pas:93                      end;
    }
end;

procedure TForm2.Button3Click(Sender: TObject);

begin
    prcRP_p(@myRecord);
    {
    Unit2.pas:112                     prcRP_p(@myRecord);
    00423500 0574040000               add    $0x474,%eax
    00423505 8b5004                   mov    0x4(%eax),%edx
    00423508 8b4808                   mov    0x8(%eax),%ecx
    0042350B 01ca                     add    %ecx,%edx
    0042350D 89500c                   mov    %edx,0xc(%eax)
    Unit2.pas:113                     end;
    }
end;

procedure TForm2.Button4Click(Sender: TObject);
begin
    prcPR_p(@myRecord);
    {
    Unit2.pas:126                     prcPR_p(@myRecord);
    00423520 0574040000               add    $0x474,%eax
    00423525 8b5004                   mov    0x4(%eax),%edx
    00423528 8b4808                   mov    0x8(%eax),%ecx
    0042352B 01ca                     add    %ecx,%edx
    0042352D 89500c                   mov    %edx,0xc(%eax)
    Unit2.pas:149                     end;
    }
end;

procedure TForm2.Button5Click(Sender: TObject);
begin
    prcPR_r(myRecord);
    {
    Unit2.pas:140                     prcPR_r(myRecord);
    00423547 0574040000               add    $0x474,%eax
    0042354C 890424                   mov    %eax,(%esp)
    0042354F 8b1c24                   mov    (%esp),%ebx
    00423552 8b0424                   mov    (%esp),%eax
    00423555 8b1424                   mov    (%esp),%edx
    00423558 8b4804                   mov    0x4(%eax),%ecx
    0042355B 8b4208                   mov    0x8(%edx),%eax
    0042355E 01c1                     add    %eax,%ecx
    00423560 894b0c                   mov    %ecx,0xc(%ebx)
    Unit2.pas:153                     end;
    }
end;

procedure TForm2.Button6Click(Sender: TObject);
begin
    with myRecord do begin
      F2:=F0+F1;
    end;
    {
    Unit2.pas:158                     with myRecord do begin
    00423570 0574040000               add    $0x474,%eax
    Unit2.pas:159                     F2:=F0+F1;
    00423575 8b5004                   mov    0x4(%eax),%edx
    00423578 8b4808                   mov    0x8(%eax),%ecx
    0042357B 01ca                     add    %ecx,%edx
    0042357D 89500c                   mov    %edx,0xc(%eax)
    Unit2.pas:164                     end;
    }
end;

{$R *.lfm}

end.                                                   

тут есть всякая фигня но интерисуют
Код: Выделить всё
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);

и принятая как эталонная копиПаст
Код: Выделить всё
procedure Button6Click(Sender: TObject);


Там в теле процедуры, в комментариях, асемблерный код из отладчика.
Просматривая его я замечаю, что вызовы (подставленный вместо их код) не индентичен, из чего делаю вывод что использование процедур из "некой Офигительной Библиотеки" НЕ равнозначно.


хотелось бы
1. услышать мнение знатоков
2. повторить свой вопрос
Brainenjii писал(а):каким образом расставить расставить модификаторы inline, const, var для достижения следующих вещей:1. сосредоточение кода в ЕДИНСТВЕННОМ месте2. оптимальное, по скорости, исполнение кода для обоих модификаций вызова.
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение hinst » 01.08.2012 22:02:34

Я же вам выше вон написал где что ставить
hinst писал(а):inline надо сделать F0, где указатель передаётся
const надо поставить в обоих процедурах, так как параметры не изменяются ни там, ни там, т. что нет смысла их копировать лишний раз (они ведь копируются если const или var не стоит)

В принципе тут inline можно поставить и у любой из двух этих процедур, F1 либо F0, но только у одной из них; разницы особой не будет, эффект должен быть примерно одинаковый. Даже лучше поставть у F1 наверное.

Итого надо поставить один inline и два const

Вообще конечно чтобы разобраться лучше знать как это всё используется и что эти процедуры делают. Можно расставлять что угодно и как угодно, я лишь привёл вариант как мне кажется правильно
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение alexey38 » 02.08.2012 05:36:47

inline - нужно ставить на f1, то есть у процедур, которые короткие, и которые по сути не содержат кода, кроме приведения типов. Используя inline мы исключаем лишний вызов процедуры и в основном коде просто будет как бы F0(@prm).

Если в f0 - кода 1 строка, то тоже можно inline, а если 100 строк, то это приведет к неоправданному росту бинарника (будет несколько сотен строк на ассемблере). Есть еще один аргумент против inline для процедур содержащих код, это кэши и оптимизация в современных процессорах. Если f0 будет часто вызываться, то как процедура это будет работать быстрее, чем дублирование кода через inline, т.к. в первом случае процессор уже разобрал код в кэше и т.п., а во-втором он будет делать это каждый раз при дублировании кода.

Что касается Const - то их нужно писать всегда, когда сам параметр не будет меняться. В случае со структурой мы тем самым при вызове f1 избежим создания копии структуры, что во-первых затратно по времени, а с другой стороны и меняет логику работы. Если в f0 идет изменение структуры, то при вызове через f1 без Var или без Const будут изменения в копии, которые просто потеряются.

Более правильно создавая процедуру сразу у каждого параметра указывать: Const, Var, Out - если нам не нужна копия. И без этих префиксов только в случае если нам нужна временная копия. Это вопрос не только быстродействия (создание копии затратно по времени), но и меняет логики работы.
alexey38
долгожитель
 
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение iN0k » 02.08.2012 08:11:34

гм ...
в ходе продолжения експериментов получил следующий факт

Код: Выделить всё
procedure prcTTT_p(const node:pMyRecord); inline;
begin
   with node^ do begin
     F2:=F0+F1;
   end;
end;

procedure prcTTT_r(var   node:rMyRecord); inline;
begin
   prcTTT_p(@node)
end;


и использование
Код: Выделить всё
procedure TForm2.Button6Click(Sender: TObject);
begin
    with myRecord do begin
      F2:=F0+F1;
    end;
    {
    Unit2.pas:158                     with myRecord do begin
    00423570 0574040000               add    $0x474,%eax
    Unit2.pas:159                     F2:=F0+F1;
    00423575 8b5004                   mov    0x4(%eax),%edx
    00423578 8b4808                   mov    0x8(%eax),%ecx
    0042357B 01ca                     add    %ecx,%edx
    0042357D 89500c                   mov    %edx,0xc(%eax)
    Unit2.pas:164                     end;
    }
end;

procedure TForm2.Button7Click(Sender: TObject);
begin
    prcTTT_p(@myRecord);
    {
    Unit2.pas:179                     prcTTT_p(@myRecord);
    00423590 057c040000               add    $0x47c,%eax
    00423595 8b5004                   mov    0x4(%eax),%edx
    00423598 8b4808                   mov    0x8(%eax),%ecx
    0042359B 01ca                     add    %ecx,%edx
    0042359D 89500c                   mov    %edx,0xc(%eax)
    Unit2.pas:180                     end;
    }
end;

procedure TForm2.Button8Click(Sender: TObject);
begin
    prcTTT_r( myRecord);
    {
    Unit2.pas:193                     prcTTT_r( myRecord);
    004235B0 057c040000               add    $0x47c,%eax
    004235B5 8b5004                   mov    0x4(%eax),%edx
    004235B8 8b4808                   mov    0x8(%eax),%ecx
    004235BB 01ca                     add    %ecx,%edx
    004235BD 89500c                   mov    %edx,0xc(%eax)
    Unit2.pas:194                     end;

    }
end;   


то есть шаманское изменение привело к интерисующему меня результату.
а шаманскому, по тому что для меня не очевидна.

может кто-нибудь разьяснить полученный результат?
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение Ask » 02.08.2012 09:07:26

Более правильно создавая процедуру сразу у каждого параметра указывать: Const, Var, Out - если нам не нужна копия.

Это не так. К сожалению, с ключевым словом "const" в Паскале связан ряд заблуждений. На самом деле:
1) Ключевое слово "const" НЕ влияет непосредственно на способ передачи параметра -- параметр может быть передан как по ссылке,
так и по значению в зависимости от платформы, настроек оптимизации и т.д.
2) Чтобы гарантировать передачу по ссылке, следует использовать модификатор "constref", но он был реализован относительно недавно, и разработчики компилятора не рекомендуют его использовать без веской причины.
3) Ключевое слово "const" интерпретируется как обещание *от программиста компилятору*, что указанная переменная не поменяется *нигде в программе* в период выполнения функции/процедуры. Обратите внимание, что именно от программиста к компилятору, а не наоборот. Компилятор проверяет выполнение этого обещания только в простейших случаях. Если оно всё же будет нарушено, это может привести к очень трудно отлаживаемым ошибкам и падениям, особенно в случае refcounted типов, таких как строки и интерфейсы.
4) Разработчики языка принципиально не будут менять перечисленные выше пункты -- на этот счёт были долгие и бурные дискуссии, их позиция окончательна.

Таким образом, можно порекомендовать использовать префикс const осторожно, относиться к нему как к оптимизации,
и уж во всяком случае не расставлять бездумно где попало -- например, современные версии Lazarus больше не добавляют "const" автоматически к
параметру setter'а свойств объекта.
К модификаторам var и out перечисленные проблемы не относятся -- их можно свободно использовать там, где они нужны по смыслу
(не путая друг с другом, конечно).
Ask
постоялец
 
Сообщения: 163
Зарегистрирован: 25.12.2008 03:51:37

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение iN0k » 02.08.2012 09:27:09

упс ...
про состояние с модификатором const и уж тем более constref не знал :(
СПАСИБО !!!

но с чем связанно
Ask писал(а):2) Чтобы гарантировать передачу по ссылке, следует использовать модификатор "constref", но он был реализован относительно недавно, и разработчики компилятора не рекомендуют его использовать без веской причины.

интерисует часть про веские причины. полистав новоСкачанную документацию таких пугалок не встретил

а где можно почитать про?
Ask писал(а):4) Разработчики языка принципиально не будут менять перечисленные выше пункты -- на этот счёт были долгие и бурные дискуссии, их позиция окончательна.


Добавлено спустя 43 минуты 18 секунд:
Re: Inline vs CopyPast и способ передачи параметров в процедуры.
тоесть получается для упрощения диалога с компилятором мне лучше трактовать модификатор "const" как МОЕ обязательство READonly ???
iN0k
постоялец
 
Сообщения: 146
Зарегистрирован: 18.07.2012 14:09:50

Re: Inline vs CopyPast и способ передачи параметров в процед

Сообщение Ask » 02.08.2012 16:51:14

часть про веские причины

В первую очередь совместимость с другими языками программирования:
http://wiki.freepascal.org/FPC_New_Features_2.6.0#Constref_parameter_modifier

где можно почитать

Например http://lists.freepascal.org/lists/fpc-devel/2011-July/024981.html

лучше трактовать модификатор "const" как МОЕ обязательство READonly

Да, но к сожалению это правило имеет недостаток -- его нарушение легко проглядеть.
Менее эффективный, но проще отслеживаемый вариант -- не использовать const
для параметров типа string, интерфейсов и объектов, а также записей, содержащих эти типы
в качестве полей.
Ask
постоялец
 
Сообщения: 163
Зарегистрирован: 25.12.2008 03:51:37

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4

Рейтинг@Mail.ru