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.