Is it possible to pass an intrinsic function as a parameter? lazarus / 62-General ---------------------------------------------------------------------- glorfin · 2019-07-25 23:37 If I define   TConversionFunction = function(X:integer):integer; and   MyProc(V:TIntegerArray; Func:TConversionFunction); then I can pass any function I write. However, if I pass an intrinsic, say, abs, I get an error: Error: Incompatible type for arg no. 2: Got "
", expected "" So, is there a way to pass such a function? Thank you in advance! ---------------------------------------------------------------------- lucamar · 2019-07-25 23:48 AFAIK not directly, but you can pass a function that itself just calls the intrinsic: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function DummyAbs(X: Integer): Integer;begin  Result := Abs(X);end; begin  AnInt := MyProc(AnInt, @DummyAbs);end. Not the ideal soultion, I know. ---------------------------------------------------------------------- glorfin · 2019-07-25 23:53 Thank you! Yes, I thought about it, but of course it will be quite slow. I am jealous to Fortran 90 programmers with direct operations on arrays, and want to have something like Apply. But then efficiency is an issue. ---------------------------------------------------------------------- lucamar · 2019-07-26 00:27 Quote from: glorfin on July 26, 2019, 01:53:15 amThank you! Yes, I thought about it, but of course it will be quite slow. I am jealous to Fortran 90 programmers with direct operations on arrays, and want to have something like Apply. But then efficiency is an issue. It won't slow you too much; after all it's just one level of indirection. It may (or not) be sped up by using: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function DummyAbs(constref X: Integer): Integer; inline;begin  Result := Abs(X);end;If the inline works (which isn't guaranteed) the indirection will dissapear and a constref might be passed in a register, which is usually quicker than pushing it to the stack. It depends on a lot of things (not the less on whether I'm correct ); you can but try various ways  of implementing it and keep the fastest. ---------------------------------------------------------------------- glorfin · 2019-07-26 00:39 Of course, another solution is to keep generic Apply function for user-supplied functions, and write for intrinsics specific procedures, something like   procedure AbsArray(V:TFloatArray); and call intrinsic directly. I will look for possibilities tomorrow. ---------------------------------------------------------------------- lucamar · 2019-07-26 00:48 Quote from: glorfin on July 26, 2019, 02:39:31 amOf course, another solution is to keep generic Apply function for user-supplied functions, and write for intrinsics specific procedures, something like [...snip...] I will look for possibilities tomorrow. Good luck! ---------------------------------------------------------------------- jamie · 2019-07-26 01:22 There is a such thing as open arrays and overloads. the compiler handles them well. ---------------------------------------------------------------------- glorfin · 2019-07-27 01:22 Finally, it looks like intrinsic functions are implemented in a special way and often inlined, so, approach of lucamar is slow. Indeed, following program Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program testcalls;{$mode objfpc}{$H+}{$inline on}uses SysUtils, DateUtils, Math;type  Float = single;  TVector = array of Float;  TTransformFunc = function(X:float):float;   function Seq(Lb, Ub : integer; first, increment:Float; Vector:TVector = nil):TVector;  var    I:integer;  begin    if Vector = nil then      SetLength(Vector, Ub+1)    else    Ub := Max(Ub,High(Vector));    if Lb <= Ub then      Vector[Lb] := 0    else      Exit;    for I := Lb+1 to Ub do                 // 2 cycles to avoid rounding error if      Vector[I] := Vector[I-1]+increment;  //First is very large and increment small    for I := Lb to Ub do      Vector[I] := Vector[I]+first;    Result := Vector;  end; procedure Apply(V: TVector; Lb, Ub: integer; Func: TTransformFunc);var  I:integer;begin  Ub := max(High(V),Ub);  for I := Lb to Ub do    V[I] := Func(V[I]);end; function EAbs(X:float):float; inline;begin  result := abs(X);end;procedure AbsArray(V:TVector; Lb, Ub: integer);var  I:integer;begin  Ub := max(High(V),Ub);  for I := Lb to Ub do    V[I] := abs(V[I]);end; var  Vec:TVector; // TVector = array of Float; Float = double  I:integer;  time1,time2:tdatetime;begin  Vec := Seq(0,256000,-3000,2); // creates array with maximal index 128000  time1 := time;                // and initializes with sequence -3000, -2998 etc  for I := 0 to 20 do    AbsArray(Vec,0,High(Vec));  time2 := time;  writeln('it takes ',inttostr(millisecondsbetween(time2, time1)), ' ms. for direct call of Abs.');  time1 := time;  for I := 0 to 20 do    Apply(Vec,0,High(Vec),@EAbs);  time2 := time;  writeln('it takes ',inttostr(millisecondsbetween(time2, time1)), ' ms. Apply of indirect call.');  readln;end.  prints: it takes 6 ms. for direct call of Abs. it takes 21 ms. Apply of indirect call. If Float = double thendifference is much bigger: it takes 7 ms. for direct call of Abs. it takes 63 ms. Apply of indirect call. and if Float = extended then it takes 20 ms. for direct call of Abs. it takes 81 ms. Apply of indirect call. So, it looks like Apply for intrunsic functions is not a good idea. ---------------------------------------------------------------------- lucamar · 2019-07-27 02:11 Woah! That's indeed a huge difference. I'd not have expected that but as they say: "seeing is believing". The ABS results are specially damning. ---------------------------------------------------------------------- PascalDragon · 2019-07-27 08:26 Well, there is an alternative: generics. The following example requires 3.2 or newer, but the code could be reworked to work in older versions as well (by using types instead of functions). It's the example from glorfin with my code added above the program's begin…end. block. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program ttestcalls;{$mode objfpc}{$H+}{$inline on}{$modeswitch advancedrecords}uses SysUtils, DateUtils, Math;type  Float = single;  TVector = array of Float;  TTransformFunc = function(X:float):float;   function Seq(Lb, Ub : integer; first, increment:Float; Vector:TVector = nil):TVector;  var    I:integer;  begin    if Vector = nil then    SetLength(Vector, Ub+1)    else      Ub := Max(Ub,High(Vector));    if Lb <= Ub then      Vector[Lb] := 0    else      Exit;    for I := Lb+1 to Ub do                 // 2 cycles to avoid rounding error if      Vector[I] := Vector[I-1]+increment;  //First is very large and increment small    for I := Lb to Ub do      Vector[I] := Vector[I]+first;    Result := Vector;  end; procedure Apply(V: TVector; Lb, Ub: integer; Func: TTransformFunc);var  I:integer;begin  Ub := max(High(V),Ub);  for I := Lb to Ub do    V[I] := Func(V[I]);end; function EAbs(X:float):float; inline;begin  result := abs(X);end;procedure AbsArray(V:TVector; Lb, Ub: integer);var  I:integer;begin  Ub := max(High(V),Ub);  for I := Lb to Ub do    V[I] := abs(V[I]);end; generic procedure ApplyFunctor(V: TVector; Lb, Ub: Integer);var  i: Integer;begin  Ub := max(High(v), Ub);  for i := Lb to Ub do    V[i] := T.Call(V[i]);end; type  TAbsFunctor = record  class function Call(aValue: Float): Float; static; inline;  end; class function TAbsFunctor.Call(aValue: Float): Float;begin  Result := Abs(aValue);end; var  Vec:TVector; // TVector = array of Float; Float = double  I:integer;  time1,time2:tdatetime;begin  Vec := Seq(0,256000,-3000,2); // creates array with maximal index 128000  time1 := time;                // and initializes with sequence -3000, -2998 etc  for I := 0 to 20 do    AbsArray(Vec,0,High(Vec));  time2 := time;  writeln('it takes ',inttostr(millisecondsbetween(time2, time1)), ' ms. for direct call of Abs.');  time1 := time;  for I := 0 to 20 do    Apply(Vec,0,High(Vec),@EAbs);  time2 := time;  writeln('it takes ',inttostr(millisecondsbetween(time2, time1)), ' ms. Apply of indirect call.');  time1 := time;  for i := 0 to 20 do    specialize ApplyFunctor(Vec,0,High(Vec));  time2 := time;  writeln('it takes ',inttostr(millisecondsbetween(time2, time1)), ' ms. Apply of functor.');  readln;end. The output then is this: Code: [Select]it takes 10 ms. for direct call of Abs. it takes 22 ms. Apply of indirect call. it takes 10 ms. Apply of functor.Note: the first and third time varies between 10 and 12 ms for both entries. And the generated assembly for the call to T.Call in ApplyFunctor is this: Code: [Select]# [57] V[i] := T.Call(V[i]); movq -8(%rbp),%rax movslq -28(%rbp),%rdx leaq (%rax,%rdx,4),%rax movss (%rax),%xmm0 andps FPC_ABSMASK_SINGLE(%rip),%xmm0 movq -8(%rbp),%rdx movslq -28(%rbp),%rax movss %xmm0,(%rdx,%rax,4) cmpl -28(%rbp),%ecx jle .Lj90 jmp .Lj88Also please note that this is not Delphi compatible as their generic implementation is less like the C++ one and more like the C# one (it will work in mode Delphi however ). ---------------------------------------------------------------------- Thaddy · 2019-07-27 09:35 I have this as an alternative abs{() Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}function abs2(const v:integer):integer;inline;begin  Result := v * -integer(v<0); // computes signend; begin  writeln(abs2(-2));end. ---------------------------------------------------------------------- LemonParty · 2019-07-27 10:02 This is a professional https://github.com/mikerabat/mrmath method about gaining the real performance. Nor generics, nor instrincs, nor even the best compilers can't do the high quality vectorization today.