Does avoiding functions increase the performance? - delphi

Here is a little test:
function inc(n:integer):integer;
begin
n := n+1;
result := n;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
start,i,n:integer;
begin
n := 0;
start := getTickCount;
for i := 0 to 10000000 do begin
inc(n);//calling inc function takes 73 ms
//n := n+1; writing it directly takes 16 ms
end;
showMessage(inttostr(getTickCount-start));
end;

Yes, calling a function introduces an overhead. Before calling the function it's necessary to save the current state - which instruction was planned to execute next - and also to copy the function parameters. This requires extra work and extra time.
That's where inlining is helpful. If the compiler supports that it can just injsct the function code directly at the call site and avoid the overhead. With good optimization of surrounding code it can even decrease amount of generated code.
This doesn't mean you need to avoid functions. In most cases the function body executes much longer that the time needed to organize the call. Only in quite rare cases the overhead is worth optimizing. This should never be done without the help of the profiler - otherwise you waste time and most likely just get a lot of unmaintainable code.

Calling a function (whichever language you're working with) generally involves doing a bit more things, like saving some context, pushing parameters to some kind of stack, calling the function itself, reading the parameters, and then pushing the result back somewhere, returning from the function, extracting the return value, ...
So, of course, calling functions generally means having some overhead.
But the main point of functions is re-using some parts of code : maybe it will take a few micro-seconds more at execution, but if you only have to write some code once, instead of 10 (or more) times, there is a huge gain ; and that code will be much easier to maintain, which is really important in the long term.
After, you might want not using functions for some really small parts of code like the one you provided as an example (well, except if the language you're using provides some kind of inlining thing -- it's the case for C, if I remember correctly ; not sure about delphi, though) : the overhead of calling the function will be important, compared to the number of lines of code the function will save you from writing (here : none ! On the contrary ^^ ).
But for bigger parts of code, the overhead will me much smaller, compared to the time taken to execute the bunch of code the function contains...

Premature optimization is the root of all evil...
Write correct and maintainable code using the known features (here the built-in pseudo(magic) procedure inc), benchmark it and refactor where it's needed for performance reason (if any).
I bet that in 99.9% of the cases, avoiding calling a function or procedure is not the solution.
Here is an example where adding a call to a procedure actually IS the optimization.

Only optimize when there is a bottleneck.
Your current code is perfectly fine for about 99.9% of the cases.
If it gets slow, use a profiler to point you at the bottleneck.
When the bottleneck appears to be in the inc function, then you can always inline your function by marking it with the 'inline' directive.
I totally agree with Francois on this one.

One of the most expensive parts of a function call is the returning of the result.
If you did want to keep your program modular, but wanted to save a bit of time, change your function to a procedure and use a var parameter to retrieve the result.
So for your example:
procedure inc(var n:integer);
begin
n := n+1;
end;
should be considerably faster than using your inc function.
Also, in the loop in your example, you have the statement:
inc(n)
but this will not update the value of n. The loop will finish and n will have the value of 0. What you need instead is:
n := inc(n);
For your timings, do you have optimization on? If you do, then it may not be timing what you thing it is. The value of n is not used by the program and may be optimized right out of it.
To make sure that n is used for the timings, you can simply display the value of n in your showMessage line.
Finally, inc is a built in procedure. It is not good practice to use the same function name as that of a built in procedure as it can cause doubts as to which procedure is being executed - yours or the built in one.
Change your function's name to myinc, and then do a third test with the built in inc procedure itself, to see if it is faster than n := n + 1;

As others before me said. Yes it does. Every line of code you write does. Functions need to store current states of registers etc... before they can execute and restore it afterwards.
But the overhead is so minimal that optimizing that means nothing. It is more important to have a redable well structured code. Almost always. There may be rare cases when every nanosecond is important but I cannot imagine one right now.
Look here for general guidelines about performance in delphi programs:
http://effovex.com/OptimalCode/opguide.htm

just want to add some comments specific to Delphi:
I think i remember than getTickCount() got a minimal resolution a bit hight to do this kind of test. (+/- 10-15ms). You could use QueryPerformanceCounter() for a better result.
for small function called a lot of time (inside process loop, data convertion, ...) use INLINE (search the help)
but to know for real what a funciton take and if you should do something about it, use a profiler !! I use http://www.prodelphi.de/, it's pretty simple, very usefull and the price is very correct compare to other profiler (ie: +/-50€ instead of 500€)
In delphi, they is the inc() function. It's faster than "n := n+1". ( because inc() is not really a function, it is replaced by the compiler by asm. ie: they is no source code for the funcion inc() ).

All good comments.
Functions are supposed to be useful, that's why they're in the language. The assumption is that if they have a nominal cost, you are willing to pay that to get the utility they provide.
Here's the real problem with functions, no matter who writes them, but especially if somebody other than you wrote them.
They have an implied contract for what they're supposed to do, but they have no contract for how long they should take.
Usually the person who writes the function thinks "This function does something valuable, so the person who calls it will respect that, and use it sparingly."
Then the person who calls it thinks "This function does so much in only a single call that I can make my code really clean and powerful by calling it lots of times."
Now, with multiple layers of abstraction, this effect acts like compound interest.
So, the real performance problem with functions is not the cost of calls, it is the psychology of programmers, leading to exponential slowdown.
Fortunately, experience in performance tuning can ameliorate this problem.

Related

Why is Sysutils.RenameFile inlined?

[dcc32 Hint] H2443 Inline function 'RenameFile' has not been expanded
because unit 'Winapi.Windows' is not specified in USES list
I understand that inlining a function makes the code faster. But I see the gain only in tight places. For example calling a small function in a big loop.
But how can inlining an IO function can improve speed? I mean by inlining RenameFile you gain few microseconds. But executing the function itself may take milliseconds, maybe even tens of ms if the disk is busy.
Even more, if you are using RenameFile, you are probably in a block of code where you are doing other I/O operations. So, this block of code will take a lot of time. So, the gain is even more insignificant now.
RenameFile is inlined because it is a simple call to another function.
Here's what it looks like:
function RenameFile(const OldName, NewName: string): Boolean;
{$IFDEF MSWINDOWS}
begin
Result := MoveFile(PChar(OldName), PChar(NewName));
end;
By inlining this function the call to SysUtils.RenameFile gets replaced by a call to WinApi.Windows.MoveFile.
This has the following advantages:
You save a call, instead of two calls you only have one call.
Your calling code is exactly the same size.
The CPU keeps a list of return addresses for branch prediction (return stack buffer); by eliminating the redundant call it saves space in this buffer this prevents misprediction if the call stack gets too deep.
The generated code is smaller, because RenameFile itself gets eliminated.
So the inlining is very much worth the trouble, especially in recursive code where the call stack can get deep the CPU will start mispredicting the returns because the return stack buffer overflows (some CPU's have only 8 entries, top of the line CPU's have 24 entries).
As a rule every routine that simply calls another routine should always be inlined.
A correctly predicted return costs a single cycle, a mispredict empties the pipeline and costs 25 cycles or more; further delays are added because the return address needs to be fetched from memory rather than the buffer.
You are correct that none of these advantages are going to matter in disk IO code, but that does not detract from the fact that simple redirect functions like this should always be inlined.

Does the compiler optimize (close) identical FieldByName calls?

In some code I'm maintaining, I see two different methods used in TClientDataSet.OnCalcFields event handlers:
with DataSet do
begin
// 1. Call FieldByName twice
if AMinDate > FieldByName(SPlanAllocatieFromDate).AsDateTime then
AMinDate := FieldByName(sPlanAllocatieFromDate).AsDateTime;
// 2. Put the retrieved FieldByName value in a temp var
lEmpID := FieldByName(SPlanAllocatieEmpID).AsInteger;
if lEmpID <> 0 then lTSAllocatedEmpIDs.Add(IntToStr(lEmpID));
end;
Will the compiler (Delphi XE2, Win32 app) optimize method 2 to use a temp var? The two FieldByNames are quite close, you could even say nested.
If not, I should rewrite 1. because OnCalcFields executes often.
BTW. I know about Fields[] versus FieldByName(), or using a temp TField var when running an EOF loop, those are not the issue here.
No version of the Delphi compiler does anything like this.
Such optimizations would require the compiler to be able to prove that the two calls to FieldByName would always give the same result, and there is currently no provision for flagging a method as being deterministic.
Note that it is quite possible in theory (if unlikely in reality) for the two calls NOT to give the same result, in this case e.g. if a different thread deletes a field out of the collection between the first and second call. Generally, the compiler does not know or care at the call site what a particular method call actually does.
Does the compiler optimize (close) identical FieldByName calls?
No it does not.
The compiler does not look inside function calls to see what is within. It therefore has no way to prove that the value returned by successive calls to a function would be the same. Likewise it has no way to prove that the function has no side-effects. These are the two prerequisites for the optimisation under consideration.
You will need to perform the optimisation yourself, by explicitly adding and using a local variable to store the value returned by a single call to FieldByName.
Beyond the consideration of performance, I would argue that the use of a local variable to hold the field is semantically much better. This makes it clear to the reader that all actions are performed on the same field. That reason alone would be enough to persuade me to make the change you describe. Don't repeat yourself.
And while we are in code review mode, you might care to reconsider the use of with.

Accessing a container object vs a locally assigned variable

Can anyone tell me if there is any performance benefit to assigning an object in the container to a local variable if its used a lot in a tight loop.
I have a large for loop and inside the loop an object from a container is access often.
i.e
for i := 0 to 100000 do
begin
my_list[i].something := something;
my_list[i].something_else := something;
my_list[i].something_else := something;
my_list[i].something_else := something;
my_list[i].something_else := something;
end;
Would I see a performance improvement by assigning
local_ref := my_list[i];
at the start of each iteration?
I am using a generic container (TList<<>MyObject<>>).
Making the change you suggest will certainly result in faster code. Accessing a local variable is always going to be faster than accessing a property getter on TList<T>. For a start, those getters perform validity checking on the index. But even for a class with the most simple getter possible, it would be hard to beat the cached local for performance.
Now, whether this matters in your case is impossible to say from here. If you do anything remotely non-trivial inside the loop then their runtime of the item getter will be irrelevant.
The fact that the loop might run for a large number of iterations is not the key factor. What counts most of all is how much time you spend in each iteration. If it costs you 1 time unit to call the item getter, and 1000 time units to do whatever you do with each item, then it the getter performance is a non-issue.
Ultimately the definitive way to answer the question is to time the alternatives. Only optimise based on measurement.
There's a much better reason to copy the item into a local variable: clarity of expression. Your current code is an egregious violation of the DRY principle.
Finally, this code would read best of all if it used a for in loop:
for Item in List do
....
Now, for in loops can be slower than traditional loops, but you should weight that against clarity and maintainability. Optimisation usually makes code harder to maintain and more prone to faults. Conclusion: only optimise bottlenecks.
It all depends on how my_list[i] is retrieved. If it results in a bunch of function calls, it can potentially make a difference (not to mention any side effect).
As usual, you should measure before doing any kind of performance refactoring. Premature optimization.....
For the record, it was one of the "good" use of with in the original Pascal design:
with my_list[i] do
begin
something := something_else;
[...]
end;

How to program a small program?

I would like to program productive and keep my FileSize very small.
However I would like to know a few tips how do accomplish that.
For example what is better for a small FileSize:
Either:
if .... = '1' then begin
...
end;
or:
if ..... = inttostr(1) then begin
...
end;
or:
if .... = inttostr($0001) then begin
...
end;
or:
case of intvar
1: ...
2: ...
end;
Then there is something that I tried and I was surprised.
I made another Unit in my project that stores Strings as constants and then I use the constant vars to replace the strings in my project. For some reason this raises my FileSize although I replace double used Strings as a var now.
Also is it better to store stuff in vars than put them directly into the code?!
For example:
Function1(param1, param2, param3); // this code is used about 20 times in my project
or is it better if I:
Avar = Function1 (param1,param2,param3); // Store this once in a var and then replace it
And what about:
if ... = TRUE
or:
if ....
Same as:
if .... = FALSE
or:
if not(...)...
Any other tips to program productive for a smaller FileSize?
Thanks in advance.
I use Delphi7
I'm sorry to be blunt, but you are putting the cart before the horse.
If you really want to know how to make your executable smaller without already knowing what differences will result from your code variations in your given examples, you should just stop right now and read/learn/practice until you know more about the language and the compiler.
Then you'll understand that your question makes little sense per se, as you can already see by all the pertinent comments you got.
the exact same source code can result in vastly different executables and different source code can result in the same executable, depending on the compiler options/optimizations
if your main goal is to micro-manage/control the generated exe, program directly in assembler.
if you want to know what is generated by the compiler, learn how to use the CPU View.
program for correctness first, then readability/maintainability
only then, if needed (implies using correct metrics/tools), you can optimize for speed, memory usage or file size (probably the least useful/needed)
Long time ago, i tried to make a program as small as possible, because it had to fit onto a floppy disk (yes i know i'm a dinosaur). This Splitter tool was written in Delphi and is about 50KB in size.
To get it this small, it was necessary to do without a lot of Delphi's units (especially the Forms unit and all units with references to it). The only way, was to use the Windows-API directly for the GUI, and i can't think of a reason to do this nowadays, but out of interest.
As soon as you use the VCL, the exe size will grow much more, than all micro optimizations in your code together can reduce it.

Why can't I use an Int64 in a for loop?

I can write for..do process for integer value..
But I can't write it for int64 value.
For example:
var
i:int64;
begin
for i:=1 to 1000 do
end;
The compiler refuses to compile this, why does it refuse?
The Delphi compiler simply does not support Int64 loop counters yet.
Loop counters in a for loop have to be integers (or smaller).
This is an optimization to speed up the execution of a for loop.
Internally Delphi always uses an Int32, because on x86 this is the fastest datatype available.
This is documented somewhere deep in the manual, but I don't have a link handy right now.
If you must have a 64 bit loop counter, use a while..do or repeat..until loop.
Even if the compiler did allow "int64" in a Delphi 7 for-loop (Delphi 7???), it probably wouldn't complete iterating through the full range until sometime after the heat death of the Sun.
So why can't you just use an "integer"?
If you must use an int64 value ... then simply use a "while" loop instead.
Problem solved :)
Why to use a Int64 on a for-loop?
Easy to answer:
There is no need to do a lot of iterations to need a Int64, just do a loop from 5E9 to 5E9+2 (three iterations in total).
It is just that values on iteration are bigger than what Int32 can hold
An example:
procedure Why_Int64_Would_Be_Great_On_For_Loop;
const
StartValue=5000000000; // Start form 5E9, 5 thousand millons
Quantity=10; // Do it ten times
var
Index:Int64;
begin
for Index:=StartValue to StartValue+Quantity-1
do begin // Bla bla bla
// Do something really fast (only ten times)
end;
end;
That code would take no time at all, it is just that index value need to be far than 32bit integer limit.
The solution is to do it with a while loop:
procedure Equivalent_For_Loop_With_Int64_Index;
const
StartValue=5000000000; // Start form 5E9, 5 thousand millons
Quantity=10; // Do it ten times
var
Index:Int64;
begin
Index:=StartValue;
while Index<=StartValue+Quantity
do begin // Bla bla bla
// Do something really fast (only ten times)
Inc(Index);
end;
end;
So why the compiler refuses to compile the foor loop, i see no real reason... any for loop can be auto-translated into a while loop... and pre-compiler could do such before compiler (like other optimizations that are done)... the only reason i see is the lazy people that creates the compiler that did not think on it.
If for is optimized and so it is only able to use 32 bit index, then if code try to use a 64 bit index it can not be so optimized, so why not let pre-compiler optimizator to chage that for us... it only gives bad image to programmers!!!
I do not want to make anyone ungry...
I only just say something obvious...
By the way, not all people start a foor loop on zero (or one) values... sometimes there is the need to start it on really huge values.
It is allways said, that if you need to do something a fixed number of times you best use for loop instead of while loop...
Also i can say something... such two versions, the for-loop and the while-loop that uses Inc(Index) are equally fast... but if you put the while-loop step as Index:=Index+1; it is slower; it is really not slower because pre-compiler optimizator see that and use Inc(Index) instead... you can see if buy doing the next:
// I will start the loop from zero, not from two, but i first do some maths to avoid pre-compiler optimizator to convert Index:=Index+Step; to Inc(Index,Step); or better optimization convert it to Inc(Index);
Index:=2;
Step:=Index-1; // Do not put Step:=1; or optimizator will do the convertion to Inc()
Index:=Step-2; // Now fix, the start, so loop will start from zero
while Index<1000000 // 1E6, one millon iterations, from 0 to 999999
do begin
// Do something
Index:=Index+Step; // Optimizator will not change this into Inc(Index), since sees that Step has changed it's value before
end;
The optimizer can see a variable do not change its value, so it can convert it to a constant, then on the increment assign if adding a constant (variable:=variable+constant) it will optimize it to Inc(variable,constant) and in the case it sees such constant is 1 it will also optimes it to Inc(variable)... and such optimizatons in low level computer language are very noticeble...
In Low level computer language:
A normal add (variable:=variable1+variable2) implies two memory reads plus one sum plus one memory write... lot of work
But if is a (variable:=variable+othervariable) it can be optimized holding variable inside the processor cache.
Also if it is a (variable:=variable1+constant) it can also be optimized by holding constant on the processor cache
And if it is (variable:=variable+constant) both are cached on processor cache, so huge fast compared with other options, no acces to RAM is needed.
In such way pre-compiler optimizer do another important optimization... for-loops index variables are holded as processor registers... much more faster than processor cache...
Most mother processor do an extra optimization as well (at hardware level, inside the processor)... some cache areas (32 bit variables for us) seen that are intensivly used are stored as special registers to fasten access... and such for-loop / while-loop indexes are ones of them... but as i said.. most mother AMD proccesors (the ones that uses MP technology does that)... i do not yet know any Intel that do that!!! such optimization is more relevant when multi-core and on super-computing... so maybe that is the reason why AMD has it and Intel not!!!
I only want to show one "why", there are a lot more... another one could be as simple as the index is stored on a database Int64 field type, etc... there are a lot of reasons i know and a lot more i did not know yet...
I hope this will help to understand the need to do a loop on a Int64 index and also how to do it without loosing speed by correctly eficiently converting loop into a while loop.
Note: For x86 compiles (not for 64bit compilation) beware that Int64 is managed internally as two Int32 parts... and when modifing values there is an extra code to do, on adds and subs it is very low, but on multiplies or divisions such extra is noticeble... but if you really need Int64 you need it, so what else to do... and imagine if you need float or double, etc...!!!

Resources