0% found this document useful (0 votes)
25 views

Ex 03

Uploaded by

skamelrech2020
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views

Ex 03

Uploaded by

skamelrech2020
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 4

110 - 04: Procedures and Functions

In this code, the TStopWatch record of the System.Diagnostics unit, a structure that
keep track of the time (or system ticks) elapsed between the Start (or StartNew)
and the Stop calls.
The form has two buttons both calling this same exact code, but one of them has
inlining disabled at the call site. Notice you need to compile with the Release config-
uration to see any difference (as inlining is a Release optimization). With twenty
million interactions (the value of the LoopCount constant), on my computer I get the
following results:
// on Windows (running in a VM)
Max on 20000000 [17]
Max off 20000000 [45]

// on Android (on device)


Max on 20000000 [280]
Max off 20000000 [376]
How can we read this data? On Windows, inlining more than doubles the execution
speed, while on Android it makes the program about 35% faster. However, on a
device the program runs much slower (an order of magnitude) so while on Windows
we shave off 30 milliseconds on my Android device this optimization saves about
100 milliseconds.
The same program does a second similar test with the Length function, a compiler-
magic function that was specifically modified to be inlined. Again the inlined version
is significantly faster on both Windows and Android:
// on Windows (running in a VM)
Length inlined 260000013 [11]
Length not inlined 260000013 [40]

// on Android (on device)


Length inlined 260000013 [401]
Length not inlined 260000013 [474]
This is the code used by this second testing loop:
var
sw: TStopWatch;
I, J: Integer;
sample: string;
begin
J := 0;
sample:= 'sample string';
sw := TStopWatch.StartNew;
for I := 0 to LoopCount do
Inc (J, Length(sample));
sw.Stop;
Show ('Length not inlined ' + IntToStr (J) +
' [' + IntToStr (sw.ElapsedMilliseconds) + '] ');
end;

Marco Cantù, Object Pascal Handbook


04: Procedures and Functions - 111

The Object Pascal compiler doesn’t define a clear cut limit on the size of a function
that can be inlined or a specific list of constructs (for or while loops, conditional
statements) that would prevent inlining. However, since inlining a large function
provides little advantage yet exposes you to the risk of some real disadvantages (in
terms of code bloat), you should avoid it.
One limitation is that the method or function cannot reference identifiers (such as
types, global variables, or functions) defined in the implementation section of the
unit, as they won’t be accessible in the call location. However, if you are calling a
local function, which happens to be inlined as well, then the compiler will accept
your request to inline your routine.
A drawback is that inlining requires more frequent recompilations of units, as when
you modify an inlined function, the code of each of the calling sites will need to be
recompiled as well. Within a unit, you might write the code of the inlined functions
before calling them, but better place them at the beginning of the implementation
section.

note Object Pascal uses a single pass compiler, so it cannot paste in the code of a function it hasn’t
compiled yet.

Within different units, you need to specifically add other units with inlined functions
to your uses statements, even if you don’t call those methods directly. Suppose your
unit A calls an inlined function defined in unit B. If this function in turn calls
another inlined function in unit C, your unit A needs to refer to C as well. If not,
you’ll see a compiler warning indicating the call was not inlined due to the missing
unit reference. A related effect is that functions are never inlined when there are cir-
cular unit references (through their implementation sections).

Advanced Features of Functions


If what I have covered so far includes the core features related to functions, there are
several advanced capabilities worth exploring. If you are really a newbie in terms of
software development, however, you might want to skip the rest of this chapter for
now and move to the next one.

Marco Cantù, Object Pascal Handbook


112 - 04: Procedures and Functions

Object Pascal Calling Conventions


Whenever your code calls a function, the two sides need to agree on the actual prac-
tical way parameters are passed from the caller to the callee, something called
calling convention. Generally, a function call takes place by passing the parameters
(and expecting the return value) via the stack memory area. However, the order in
which the parameters and return value are placed on the stack changes depending
on the programming language and platform, with most languages capable of using
multiple different calling conventions.
A long time ago, the 32-bit version of Delphi introduced a new approach to passing
parameters, known as “fastcall”: Whenever possible, up to three parameters can be
passed in CPU registers, making the function call much faster. Object Pascal uses
this fast calling convention by default although it can also be requested by using the
register keyword.
The problem is that this is the default convention, and functions using it are not
compatible with external libraries, like Windows API functions in Win32. The func-
tions of the Win32 API must be declared using the stdcall (standard call) calling
convention, a mixture of the original pascal calling convention of the Win16 API
and the cdecl calling convention of the C language. All of these calling conventions
are supported in Object Pascal, but you'll rarely use something different than the
default unless you need to invoke a library written in a different language, like a sys-
tem library.
The typical case you need to move away from the default fast calling convention is
when you need to call the native API of a platform, which requires a different calling
convention depending on the operating system. Even Win64 uses a different model
to Win32, so Object Pascal supports many different options, not really worth detail-
ing here. Mobile operating systems, instead, tend to expose classes, rather than
native functions, although the issue of respecting a given calling convention has to
be taken into account even in that scenario.

Procedural Types
Another unique feature of Object Pascal is the presence of procedural types. These
are really an advanced language topic, which only a few programmers will use regu-
larly. However, since we will discuss related topics in later chapters (specifically,
method pointers, a technique heavily used by the environment to define event han-
dlers, and anonymous methods), it's worth giving a quick look at them here.

Marco Cantù, Object Pascal Handbook


04: Procedures and Functions - 113

In Object Pascal (but not in the more traditional Pascal language) there is the con-
cept of a procedural type (which is similar to the C language concept of a function
pointer – something languages like C# and Java have dropped, because it is tied to
global functions).
The declaration of a procedural type indicates the list of parameters and, in the case
of a function, the return type. For example, you can declare a new procedural type,
with an Integer parameter passed by reference, with this code:
type
TIntProc = procedure (var Num: Integer);
This procedural type is compatible with any routine having exactly the same param-
eters (or the same function signature to use C jargon). Here is an example of a
compatible routine:
procedure DoubleTheValue (var Value: Integer);
begin
Value := Value * 2;
end;
Procedural types can be used for two different purposes: you can declare variables of
a procedural type or pass a procedural type (that is, a function pointer) as parameter
to another routine. Given the preceding type and procedure declarations, you can
write this code:
var
IP: TIntProc;
X: Integer;
begin
IP := DoubleTheValue;
X := 5;
IP (X);
end;
This code has the same effect as the following shorter version:
var
X: Integer;
begin
X := 5;
DoubleTheValue (X);
end;
The first version is clearly more complex, so why and when should we use it? There
are cases in which being able to decide which function to call and actually calling it
later on can be very powerful. It is possible to build a complex example showing this
approach. However, I prefer to let you explore a fairly simple application project,
called ProcType.

Marco Cantù, Object Pascal Handbook

You might also like