Probably a stupid question, but it's an idle curiosity for me.
I've got a bit of Delphi code that looks like this;
const
KeyRepeatBit = 30;
...
// if bit 30 of lParam is set, mark this message as handled
if (Msg.lParam and (1 shl KeyRepeatBit) > 0) then
Handled:=true;
...
(the purpose of the code isn't really important)
Does the compiler see "(1 shl KeyRepeatBit)" as something that can be computed at compile time, and thus it becomes a constant? If not, would there be anything to gain by working it out as a number and replacing the expression with a number?
Yes, the compiler evaluates the expression at compile time and uses the result value as a constant. There's no gain in declaring another constant with the result value yourself.
EDIT: The_Fox is correct. Assignable typed constants (see {$J+} compiler directive) are not treated as constants and the expression is evaluated at runtime in that case.
You can make sure iike this, for readability alone:
const
KeyRepeatBit = 30;
KeyRepeatMask = 1 shl KeyRepeatBit ;
It converts it to a constant at compile time.
However, even if it didn't, this would have no noticeable impact on your application's performance.
You might handle a few thousand messages per second if your app is busy. Your old Pentium I can do gazillions of shifts and ands per second.
Keep your code readable, and profile it to find bottlenecks that you then optimize - usually by looking at the algorithm, and not such a low level as whether you're shifting or not.
I doubt that using a number (would be 1073741824, by the way) here would really improve performance. You seem to be in some Windows message context here and this will possible add more delay than a single and that is lightning fast even if the number is not optimized at compiled time (anyway, I think it is optimized).
The only exception I could imagine would be the case that this particular piece of code is run really often, but as I said I think this gets optimized at compile time and so even in this case it won't make a difference at all.
Maybe it's offtopic to your question but I use a case record for these kind of things, example:
TlParamRecord = record
case Integer of
0: (
RepeatCount: Word;
ScanCode: Byte;
Flags: Set of (lpfExtended, lpfReserved=4, lpfContextCode,
lpfPreviousKeyState, lpfTransitionState);
);
1: (lParam: LPARAM);
end;
see article on my blog for more details
Related
I can't find the implementation of AtomicCmpExchange (seems to be hidden), so I don't know what it does.
Is AtomicCmpExchange reliable on all platforms? How is it implemented internally? Does it use something like a critical section?
I have this scenario :
MainThread:
Target := 1;
Thread1:
x := AtomicCmpExchange(Target, 0, 0);
Thread2:
Target := 2;
Thread3:
Target := 3;
Will x always be an integer 1, 2 or 3, or could it be something else? I mean, even if the AtomicCmpExchange(Target, 0, 0) failed to exchange the value, does it return a "valid" integer (I mean, not a half-read integer, for exemple if another thread has already started to half write of the value)?
I want to avoid using a critical section, I need maximum speed.
AtomicCmpExchange is what is known as an intrinsic routine, or a standard function. It is intrinsically known to the compiler and may or may not have a visible implementation. For example, Writeln is a standard function, but you won't find a single implementation for it. The compiler breaks it up into multiple calls to lower-level functions in System.pas. Some standard functions, such as Inc() and Dec() don't have any implementation in System.pas. The compiler will generate machine instructions which amount to simple INC or DEC instructions.
Like Inc() or Dec(), AtomicCmpExchange() is implemented using whatever code is needed for a given platform. It will generate inline instructions. For x86/x64 it will generate a CMPXCHG instruction (along with whatever setup is necessary to get variables/values into the registers). For ARM it will generate a few more instructions around the LDREX and STREX instructions.
So the direct answer to your question is that even calling into assembly code, you cannot get much more efficient than using that standard function along with others such as AtomicIncrement, AtomicDecrement, and AtomicExchange.
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.
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...!!!
How can one switch off range checking for a part of a file. Switching off is easy, but how do I revert to the project setting later on? The pseudo-code below should explain it:
Unit1;
//here's range checking on or off as per the project setting
code here...
{$R-}
//range checking is off here because the code causes range check errors
code here...
//now I want to revert to the project setting. How do I do that?
code here...
end.
See: IFOPT directive.
{$IFOPT R+}
{$DEFINE RANGEON}
{$R-}
{$ELSE}
{$UNDEF RANGEON}
{$ENDIF}
//range checking is off here because the code causes range check errors
//code here...
{$IFDEF RANGEON}
{$R+}
{$UNDEF RANGEON}
{$ENDIF}
Wrap your code in $R directives:
{$R-} // disable range checking
// do non-range-checked operations here
{$R+} // turn range checking back on
Note that the directive applies at the statement level. You cannot wrap just part of an expression with that.
Why would you want to turn it off for release builds? – dan-gph
I have seen too many Delphi programmers writing quite large programs without ever activating Range, Overflow and Assertion checking. Of course, you can do that if you want, but your code will be more buggy.
I hope to convince more programmers to enable these 3 checking right now, to make the program more reliable.
However, note that there is a price to pay for that: your program will be slower. I show below some actual time comparison of code running with and without range checking.
the point is you can turn it off locally where you need to with {$R-}.But you can leave it on globally in the project settings –
dan-gph
Personally, next to Debug and Release, I have a 3rd option called PreRelease. This is actually a Debug version with one single setting different: the "Optimizations" is on. It is fast enough while it still does the checking (range, overflow, assertions, etc).
I release this kind of version to a limited number of customers. If all seems good after one week, I replace it with the true Release version, where the checkings are off.
Overflow checking
This will check certain integer arithmetic operations (+, -, *, Abs, Sqr, Succ, Pred, Inc, and Dec) for overflow. For example, after a + (addition) operation the compiler will insert additional binary code that verifies that the result of the operation is within the supported range.
An "integer overflow" occurs when an operation on an integer variable produces a result that is outside the range of that variable. For example, if an integer variable is declared as a 16-bit signed integer, its value can range from -32768 to 32767. If an operation on this variable produces a result greater than 32767 or less than -32768, an integer overflow has occurred.
When an integer overflow occurs, the result of the operation is undefined and can lead to undefined behavior in the program:
• Wrap-around
The result might result in a wrapped-around value. This means that the number 32768 will be actually stored as 1 since it is 1 unit higher than the highest value we can store (32767).
• Truncation
The result may be truncated or otherwise modified to fit within the range of the integer type. For example, the number 32768 will be actually stored as 32767 since that is the highest value we can store.
Undefined program behavior is one of the worst kind of errors, because it is not an error easy to reproduce. Therefore, it is difficult to track and repair.
There is a small price to pay if you activate this: the speed of the program will decrease slightly.
IO checking
Checks the result of an I/O operation. If an I/O operation fails, an exception is raised. If this switch is off, we must check for I/O errors manually.
There is a minor price to pay if you activate this: the speed of the program will decrease, but insignificantly because the few microseconds introduced by this check is nothing compared with the millisecond-range time required by the I/O operation itself (the hard drives are slow).
Range Checking
The Delphi Geek calls this “The most important Delphi setting” and I totally agree. It checks if all array and string indexing expressions are within the defined bounds. It also checks that all assignments to scalar and subrange variables are within range.
Here is an example of code that would ruin our life if Range Checking would not be available:
Type
Pasword= array [1..10] of byte; // we define an array of 10 elements
…
x:= Pasword[20]; // Range Checking will prevent the program from accessing element 20 (ERangecheckError exception is raised). Security breach avoided. Error log automatically sent to the programmer. Bruce Willis saves everyone.
Enabling Runtime Error Checking
To activate the Runtime Error Checking, go to Project Options and check these three boxes:
Enabling the Runtime Error Checking in ‘Project Options’
Assertions
A good programmer MUST use assertions in its code to increase the quality and stability of the program. Seriously man! You really need to use them.
Assertions are used to check for conditions that should always be true at a certain point in the program, and to raise an exception if the condition is not met. The Assert procedure, which is defined in the SysUtils unit, is typically used to perform assertions.
You can think of assertions as poor man’s unit testing. I strongly advise you to look deeper into assertions. They are very useful and do not require as much work as unit testing.
Typical example:
SysUtils.Assert(Input <> Nil, ‘The input should not be nil!’);
But for the program to check our assertions, we need to active this feature in the Project Settings -> Compiler Options, otherwise they will simply be ignored, like they are not there in our code. Make sure that you understand the implications of what I just said! For example, we screw up badly if we call routines that have side effects in the Assert. In the example below, during Debugging when the assertions are on, the Test() function will be executed and 'This was executed' will appear in the Memo. However, during Release more, that text will not appear in the memo because now Assert is simply ignored. Congratulations we just made the program to behave differently in debug/release mode ☹.
function TMainForm.Test: Boolean;
begin
Result:= FALSE;
mmo.Lines.Add('This was executed');
end;
procedure TMainForm.Start;
VAR x: Integer;
begin
x:= 0;
if x= 0
then Assert(Test(), 'nope');
end;
Here are a few examples of how it can be used:
1 To check if an input parameter is within the 0..100 range:
procedure DoSomething(value: Integer);
begin
Assert((value >= 0) and (value <= 100), 'Value out of range');
…
end;
2 To check if a pointer is not nil before using it:
Var p: Pointer;
Begin
p := GetPointer;
Assert(Assigned(p), 'Pointer is nil');
…
End;
3 To check if a variable has a certain value before proceeding:
var i: Integer;
begin
i := GetValue;
Assert(i = 42, 'Incorrect response to “What is the answer to life”!');
…
end;
Assertions can also be disabled by defining the NDEBUG symbol in the project options or by using the {$D-} compiler directives.
We can also use the Assert as a more elegant way of handling errors and exceptions in some cases, as it can be more readable and it also includes a custom message, that would help the developer understand what went wrong.
Personally, I use it a lot at the top of my routines to check if the parameters are valid.
Activating this feature will (naturally) make your program slower because… well, there is one extra line of code to execute.
Nothing comes for free
Everything nice comes with a price (fortunately a small price in our case): enabling Runtime error checking and Assertions slows down our program and makes it somewhat larger.
Computers today have lots of RAM so the slight increase in size is irrelevant, so, let’s put that aside. But let’s look at the speed, because that is not something we can easily ignore:
Type Disabled Enabled
Range checking 73ms 120ms
Overflow checking 580ms 680ms
I/O checking Not tested Not tested
As we can see the program's speed is strongly impacted by these runtime checking. If we have a program where speed is critical, we better activate “Runtime error checking” during debugging only. What I do, I also leave it active in the first release and wait a few weeks. If no bugs are reported, then I release an update in which “Runtime error checking” is off.
Personally, I leave the “IO checking” always active. The performance hit because of this check is microscopic.
Big warning:
If you have an existing project that was not so nicely written, and you activate any of the Runtime Error checking below, your program may will crash more often than usual.
No, the Runtime Error checking routines did not break your program. It was always broken – you just didn’t know. The Runtime Checking routines are now finding all those places where the code is fishy and shitty and smelly. The sole purpose of Runtime Checking is to find bugs in your program.
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.