PDA

View Full Version : "absolute" and "register" keywords?



arthurprs
18-07-2008, 06:06 PM
very uncommon, what is the use for them?

Brainer
18-07-2008, 06:55 PM
Absolute allows you to define an address in memory where you want to have the first byte of the variable. Syntax:

var
Variable: <type> absolute segment:offset;

Register allows you to register the convention call to define the parameters of the procedures and functions. The first three parameters of functions or procedures bearing the directive will be available via the processor registers, in turn EAX, EDX, ECX, and other parameters will go to the stack in the order from left to right. Example:

procedure Foo(A, B, C, D, E: Integer); register;
begin

end;

In this procedure, the value of A parameter will be in the register EAX, B - EDX, C - ECX, D and E (in that order) will go to the stack.

JernejL
18-07-2008, 07:13 PM
example of ABSOLUTE usage as code optimization trick: this function uses a longword integer type at the float parameter address to treat data as integer in order to perform bit operations without typecasting or conversion, this code will beat "result:= x > 0" any time.



// true = positive, false = positive
function GetSign&#40;x&#58; single&#41;&#58; boolean;
var
i&#58; longword absolute x;
begin
Result &#58;= i and $80000000 = 0; // integer operator tricks
end;

You can also come up with code like this, which will use a single vector type and work with ANY memory compatible vector formats (think compatibility between glscene vector types and some other code vector types)


function DotProduct&#40;const vA, vB&#41;&#58; single; overload;
var
A&#58; Vector absolute va;
B&#58; Vector absolute vb;
begin
Result &#58;= A.X * B.X + A.Y * B.Y + A.Z * B.Z;
end;

arthurprs
18-07-2008, 08:52 PM
interesting :?

pstudio
18-07-2008, 09:39 PM
Just curious
Is it legal to use absolute to make a variable begin from adress 0?

waran
18-07-2008, 10:30 PM
It is legal, but the address is read and write protected (program will crash).

As for register: This is the default calling convention in delphi/pascal. So if
you don't specify anything its register - different to C where parameters are
always passed via stack unless you specify __fastcall as calling convention.

JernejL
18-07-2008, 11:54 PM
Just curious
Is it legal to use absolute to make a variable begin from adress 0?

Technically you can make it anywhere, this was useful in DOS times to read bios stuff which was at constant addresses, this is no longer useful today and absolute serves more for cases which i demonstrated.

arthurprs
19-07-2008, 12:10 AM
It is legal, but the address is read and write protected (program will crash).

As for register: This is the default calling convention in delphi/pascal. So if
you don't specify anything its register - different to C where parameters are
always passed via stack unless you specify __fastcall as calling convention.

so i can optimize functions using different calling conventions?

examples?

edit:

i did some tests,
seems like fastcall or register is the slower ? why ?

program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils, Windows;

type
TPoint4 = array[0..3] of Single;

procedure Test1(const a,b : TPoint4);
begin
end;

procedure Test2(const a,b : TPoint4); stdcall;
begin
end;

procedure Test3(const a,b : TPoint4); cdecl;
begin
end;

var
a,b : TPoint4;
c : Cardinal;
n : Int64;
begin
n := 0;
c := GetTickCount;
repeat
Test3(a,b);
Inc(n);
until GetTickCount >= c + 1000;
Writeln(n);

n := 0;
c := GetTickCount;
repeat
Test2(a,b);
Inc(n);
until GetTickCount >= c + 1000;
Writeln(n);

n := 0;
c := GetTickCount;
repeat
Test1(a,b);
Inc(n);
until GetTickCount >= c + 1000;
Writeln(n);

Readln;
end.


results:
123686244
130016857
59970038

JernejL
19-07-2008, 08:52 AM
try making the functions actually do something? try passing small data, large several MB of data etc.. and see hot it behaves

waran
19-07-2008, 09:58 AM
I see 2 problems with your test code:

First of all you are using GetTickCount. Its propably the most unprecise
counter available. Do this:

uses windows;

var
t1, t2, fq: int64;

begin
QueryPerformanceFrequency(fq);
QueryPerformanceCounter(t1);
YourTestCode;
QueryPerformanceCounter(t2);
writeln(((t2-t1)/fq):8:8);
end.

And second of all:
The parameters you pass are way too big to fit 2 (or 3, in some
pascal implementations) registers - so the performance improvement,
if any, is very narrow.
It could be that managing to push params over stack and registers
requires some more administration to the compiler.

And yes: Try to make the functions do something (if you use less data,
2x32Bit, then its the data is available immediately because it has not to
be popped from the stack first).

Edit:
I see you are using const. Then forget about my size matters :D

JernejL
19-07-2008, 02:14 PM
About QueryPerformanceCounter and it's own set of problems..
http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
http://www.gamedev.net/community/forums/topic.asp?topic_id=357026

Do you still claim that gettickcount is imprecise? think again.

waran
19-07-2008, 02:39 PM
Its not a matter of claiming or thinking, its a matter of facts.

GetTickCount may not suffer from bugs in hardware but its resolution is definitely 1ms,
which is far too crude to time function calls (especially in the way arthurprs does).

8)

Legolas
19-07-2008, 03:39 PM
Just curious
Is it legal to use absolute to make a variable begin from adress 0?

Technically you can make it anywhere, this was useful in DOS times to read bios stuff which was at constant addresses, this is no longer useful today and absolute serves more for cases which i demonstrated.

That, or in console development, where absolute is used for accessing video memory areas, for example :)

arthurprs
19-07-2008, 06:39 PM
new code

program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils,
Windows,
Math;

type
TPoint = record
x,y : Single;
end;
TPoint4 = array[0..3] of TPoint;

procedure Test1(const a, b: TPoint4);
var
c : Single;
begin
c := a[0].x + b[3].x;
Power(c,2);
end;

procedure Test2(const a, b: TPoint4); stdcall;
var
c : Single;
begin
c := a[0].x + b[3].x;
Power(c,2);
end;

procedure Test3(const a, b: TPoint4); cdecl;
var
c : Single;
begin
c := a[0].x + b[3].x;
Power(c,2);
end;


const
s = 10000000;

var
a, b: TPoint4;
t1, t2, fq, n: Int64;
begin
QueryPerformanceFrequency(fq);

n := 0;
QueryPerformanceCounter(t1);
repeat
Test3(a, b);
Inc(n);
QueryPerformanceCounter(t2);
until t2 >= t1 + s;
Writeln(n);

n := 0;
QueryPerformanceCounter(t1);
repeat
Test2(a, b);
Inc(n);
QueryPerformanceCounter(t2);
until t2 >= t1 + s;
Writeln(n);

n := 0;
QueryPerformanceCounter(t1);
repeat
Test1(a, b);
Inc(n);
QueryPerformanceCounter(t2);
until t2 >= t1 + s;
Writeln(n);

n := 0;
QueryPerformanceCounter(t1);
repeat
Test3(a, b);
Inc(n);
QueryPerformanceCounter(t2);
until t2 >= t1 + s;
Writeln(n);

n := 0;
QueryPerformanceCounter(t1);
repeat
Test2(a, b);
Inc(n);
QueryPerformanceCounter(t2);
until t2 >= t1 + s;
Writeln(n);

n := 0;
QueryPerformanceCounter(t1);
repeat
Test1(a, b);
Inc(n);
QueryPerformanceCounter(t2);
until t2 >= t1 + s;
Writeln(n);

Writeln('end'); Readln;
end.

fastcall is really faster (difference is VERY small)

"1632286
1548087
1678139
1564791
1637757
1672389
end"

waran
19-07-2008, 06:59 PM
Your results are similar to my own. All calling conventions are equally fast :)
However most of your bench time is taken by the loop, not the call.

masonwheeler
23-07-2008, 03:44 AM
You're not likely to notice much of a difference in most cases.

Standard calls place all the parameters on the stack when you call the function, then pops them when you return. With a register (fastcall) function, it places the first three (I believe) parameters into CPU registers, saving you from the overhead of creating the stack frame if you use 3 or less parameters.

Thing is, PUSH and POP are very fast operations anyway, and a lot of Delphi calls will use more than 3 parameters. Any object method passes "self" as an implicit parameter, and constructors pass a hidden boolean variable that's needed for inheritance. And since most things in Delphi use objects, any method call with more than 2 parameters declared will create a stack frame.

That having been said, even a small performance gain is worth taking, on general principle, and if you're using a function in an inner loop, the sort of thing that gets called all the time in your program, especially if it's a very short function, you'll want to make sure it's declared register and has a short enough parameter list to benefit from it, since those few processor cycles can add up over a few million calls.