Inline VS Linear String processing performance in Object Oriented Pascal - Delphi - performance

Because of the close ending of one of my projects I wanned set up some discussion ( in case Rob does not provide very detailed answer :D ), I am more focusing of some memory and cycle optimizations in some hungry string processing areas. In my case I am interested in some performace tests, if anyone has made something like that, for particularry performance diff for two cases:
Case 1: I use string processing in in-line way so I have one extra lengthy line, for example,
RichEdit1.SelText := stringfunction1(stringfunction2(stringfunction3(stringfunction4, stringfunction5), stringfunction6, stringfunction7(stringfunction8))))
or
Case 2:
I just split all those functions so each has executed in seperate line and therefore I have to declare the variable that would buffer the return of each function.
P.S. I hope I have not mistaken with the brackets in Case 1.
So, what is your findings / opinion / critics about this question?
Maybe it is not simply worth time to gain some extra nanosecond?

Declaring variables wouldn't make any difference I believe.
When you call functions like this, the compiler needs to generate implicit string variables to keep the result of your functions. The way you are doing it, the main advantage would be that the compiler can decide to reuse a temp variable once it's done using it, but nothing prevent the compiler to do the same with explicit variables.
Actually, every time you call a function with a string result, the compiler need to create a temp variables, because function returning a string are actually implemented as a procedure with an additional var parameter.
For exemple:
function GetTempPath : string;
is really implemented this way
procedure GetTempPath(var S : string);
so, given following procedure:
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Text := GetTempPath;
end;
The compiler first allocate a temporary string variable. Calls GetTempPath with said temp variable in parameter. Once it returns, it takes this variable and set it to Memo1.Lines.Text. Essentially, what it really does is this:
procedure TForm1.Button1Click(Sender: TObject);
var S : string;
begin
GetTempPath(S);
Memo1.Lines.Text := S;
end;
and if you actually declare the function like the following, the compiler is smart enough to not create an additionnal variable.
procedure TForm1.Button1Click(Sender: TObject);
var S : string;
begin
S := GetTempPath;
Memo1.Lines.Text := S;
end;

The code
var
s1, s2: string;
begin
s1 := 'This is a very long string...';
s2 := s1;
end;
does not copy the string s1 into s2 (which could be a performance issue in a tight loop), but it simply instructs s2 to point to the same location in memory as s1. That is, in general, assigning strings to variables isn't a very bad thing.
In fact, I am not sure what method would produce the most efficient assembly code (if they are not identical!). Even in the in-lined case, the intermediate results have to be stored somewhere...
All-in-all, I definitely think you should go for the approach that is the most readble one (to a human programmer). The difference in performance should not even be detectable.

Okay.
I will try to put things together within two sentences. :)
String optimization
basically is premature optimmization
because of fact that it is bottleneck
of performance in VERY VERY VERY rear
situations or usecases.
Inline string usages main advantage is to use compilers features that allow to reuse previous return ( temp ) variables in further paramteric function calls. Still - if we intend to use linear operation(s), we should add GetTempPath() procedure before main string equalization code to make sure we use old temp variables still accessible in memory.

Related

Confused about type coercion in function calls (stack frames)

Consider the following example:
create or replace function f(n integer) return integer as
begin
return n;
end;
/
begin
dbms_output.put_line(f(3.8));
end;
/
3.8
PL/SQL procedure successfully completed.
This makes no sense to me. Obviously, PL/SQL simply ignores the integer specification, both on entering the function and on exiting it. Is this simply a bug? Is it a design choice, made deliberately by the language developers?
Here is why I find this confusing. Compare to the following example:
declare
x integer;
begin
x := 3.8;
dbms_output.put_line(x);
end;
/
4
PL/SQL procedure successfully completed.
In this example, the data type specification is complied with. PL/SQL doesn't throw an error, but at least it performs an implicit coercion and it does not violate the data type declared for x - the variable stores the value 4, an integer, not 3.8.
So, how does PL/SQL do the function call thing in the first example? As far as I understand (never having been trained formally in computing), whenever the compiler or interpreter finds a function call it creates a stack frame, with variables for the arguments passed to the function and for the return value to come back from the function. Aren't these variables, when the stack frame is created, supposed to be the same data type as specified in the function declaration? If the stack frame has a field of integer data type for the argument 3.8, how come that is not coerced to 4 before it is even stored in the corresponding variable? And the same thing for the return value: if the function returns 3.8 but the caller expects an integer (and therefore the corresponding variable in the stack frame should be integer), how is it able to accept the return value 3.8?
And, most disturbing - why is this behavior different from the behavior when explicitly declared variables are involved (as in my second example)?
Thank you for sharing your thoughts!
The answer is found in the documentation for Oracle Database (to which your question has absolutely no relation whatsoever).
Firstly, INTEGER in the said database is a synonym for NUMBER(38). Upon assignment to a NUMBER(38) variable x, as in your second example, according to the assignment rules the NUMBER (with arbitrary precision) literal 3.8 is rounded.
In your first example though no assignment happens because IN parameters to PL/SQL subprograms are passed by reference and the same reference (to the NUMBER value 3.8) is returned.

fpc: how to initialize a global variable before it is initialized

I am making a simple text game in pascal (a real beginner one). There is a general routine, that is repeated several times (the cycles variable, representing the levels). In the beginning of the routine there is a part where character`s name is asked. If the general repeat loop is complete or aborted at some level(1-4), the game goes back to the first sort of menu. I want the name to be asked only the first time, but, of course, I get the "variable "cycles" does not seem to be initialized" warning. Is there a way to restructure the code to avoid it?
Thanks.
The code excerpt (unnecessary details left behind):
program rpg_text_game;
var
game_action:char;
name:string;
cycles:1..5;
begin
repeat
writeln('Welcome to the game.');
writeln('To continue press "g",');
writeln('to read the license of this game press "i",');
writeln('and to quit press "q" and "enter": ');
readln(game_action);
case game_action of
'i', 'I':
{shows license}
'g', 'G':
{game begins}
if not (cycles in [2,3,4,5]) then
begin
writeln('Please enter your name: ');
readln(name);
end;
repeat
cycles:=1; //is initialized here
{actual game process - score is calculated based on *cycles* amount, that adds +1 with each tick ("if success then cycles:=cycles+1")}
{cycles - 1,2,3,4,5}
writeln('Do you want to try again, y/n?');
readln(game_action);
until(game_action='n') or (game_action='N');
until (game_action='q') or (game_action='Q');
writeln();
writeln('Press enter to quit');
readln();
end.
So, how to initialize/change the cycles variable (or even any other) to avoid that message and not to cheat by turning off the compiler hint option?
If cycles is a global variable, like in your example code, then simply do, in the main block of the program, before you start anything:
begin
cycles := 1;
game_action := Chr(0);
{ etc... }
...
end.
That is how you generally initialize global variables: in the main begin/end. block. Some versions of Pascal also allow (for global variables):
var
cycles: 1..5 = 1;
{ etc... }
but others don't. I don't know if your Pascal allows it. If it does, you won't have to initialize in the main block anymore. But note that that probably doesn't work for local variables of a function or procedure. There, you will probably have to use the (outer) begin/end; block of the function or procedure.
FWIW, the main block of a program can usually be found at the very end of the program, after all the const, type, var, procedure and function declarations and it ends with a dot (.).
Also note that the comment is right: split your program into separate functions and procedures, each with their own single task, and pass any information necessary to them. Do not write monolithic blocks of code. That is hard to read and hard to maintain. For instance, for each (or most) of your case items, create a separate procedure with the necessary parameters and call those from your case statement. That makes your code much easier to read, also for you.
In the first iteration of the loop in the code as is, cycles is read (by the IF NOT (cycles in [])) before being initialized. The compiler rightfully emits a warning for that.
The solution is simple, initialize it before the first REPEAT, or if you go more object pascal style, like Rudy says.

Sqrt function in Delphi

As an exercise that put myself into, I wanted to design a calculator in Delphi. So, far the sum, subs tract, multiply and divided are pretty much working.
The only function that I have a problem with its with Square Root.
Variables are extended, and I pretty much just convert from String (I'm using two EditBox) to Float
var
Form1: TForm1;
a, b, r: Extended;
procedure TForm1.SqrtClick(Sender: TObject);
begin
a := StrToFloat(Edit1.Text);
r := Sqrt(a);
ShowMessage(FloatToStr(r));
end;
Delphi its returning me
[Error] calc.dpr(72): Missing operator or semicolon.
The problem isn't really visible in your snippet above. But since I got psychic powers, I can still tell what is going on here.
You have a button called sqrt on your form. Hence, when you write sqrt in code, it refers to the button, not to the RTL function.
Solution: Write System.Sqrt instead of Sqrt (=Self.Sqrt, the button), or rename the button.

Can I set a state within a function?

I have this code:
procedure EstablishCommunication;
var
State : TStates;
Attempts : Byte;
procedure IncAttempts;
begin
Inc(Attempts);
end;
begin
State := stReadDeviceID;
Attempts := 0;
while True do
begin
if Attempts >= MAX_ATTEMPTS then
begin
State := stError;
end;
case State of
stReadDeviceID:
begin
// some code
IncAttempts;
end;
stError:
begin
// Error code
end;
...
...
...
I'd like to put the code that set state to stError within of the procedure IncAttempts, resulting:
procedure EstablishCommunication;
var
State : TStates;
Attempts : Byte;
procedure IncAttempts;
begin
Inc(Attempts);
if Attempts >= MAX_ATTEMPTS then
begin
State := stError;
end;
end;
begin
State := stReadDeviceID;
Attempts := 0;
while True do
begin
case State of
stReadDeviceID:
begin
// some code
IncAttempts;
end;
stError:
begin
// Error code
end;
...
...
...
So, can I move the code to IncAttempts?
Is this a code smell?
If yes, Can you advice me a better way?
I would see this as perfect valid code. I ask myself the following questions when declaring a method inside another. Most of the time I don't do it, but sometimes it's results in better code.
Will the internal function ever need to change as in a descendant class?
Can I override External method without calling the internal method and be OK?
Does the internal function have practical application outside of external method?
Is the internal function complex enough that it should be unit tested outside the scope of there external method?
If any of the above apply don't use an Internal Method.
However if if you don't have any of the above, and it can remove repeated code and/or simplify the design then you can consider using a internal function.
No real problem with that, should work just fine. You are already modifying another local variable Attempts so there is no reason why modifying State should smell more.
I do think you should be careful of using inline functions to much. The code often ends up hard to read/understand.
I would say that the new code have some whiff...
It all depends on how many states you manage in current code, and if the number of states could change in the future. Beware of how and when you set the state, and beware of how and when you check the state.
In the two code snippets you show, there is a minor difference:
In the first, original code, the current state is preserved through the iteration, and the new error-state is set in the beginning of the iteration, and it is always checked.
In the second, refactored code, the state is changed in the middle of the iteration, and it is only altered if the state is stReadDeviceID.
Now, if the last line in this while True do-iteration is if State = stError then Break;, then your first code will run the iteration one more time, changing the state to stError in the beginning if the iteration. Your second code will exit at the end of the current iteration, and the code in the stError-section of the case-statement will never be executed...
If you want to go all the way, and study the GoF's State Design Pattern, then take a look at these pages:
http://en.wikipedia.org/wiki/State_pattern (no Delphi code...)
http://sourcemaking.com/design_patterns/state (with Delphi code!)
http://www.dofactory.com/Patterns/PatternState.aspx (no Delphi code...)
http://conferences.embarcadero.com/article/32129#_Toc12157322 (with Delphi code!)

Does a begin-end block affect the performance of a conditional statement?

I am working with Delphi. Does it make any difference in performance if we write if condition in different ways? For example:
if (condition) then
someVar := someVal
else
someVar := someOtherVal;
Or we can write:
if (condition) then begin
someVar := someVal;
end else begin
someVar := someOtherVal;
end;
I prefer the second option just because it looks better than the first one.
No, there is no difference in performance, the code created will be identical.
An aspect that might be more important than that the second option looks nicer, is that it is better for maintainence. If you need to add another statement in the else block, you will not accidentally forget to add the begin and end, which would put the statement outside the if and always be executed.
This will not make a difference in performance.
begin and end tell the compiler where a block of code starts and finishes, but no computation needs to be done there.
Begin and End do not slow down your code, as others have already said. I am writing another answer to encourage you even more explicitly to ALWAYS use begin and end whenever you could use them.
It is good to be liberal with using Begin and End, and not worry about them slowing you down (because they don't).
If you go the other way, and leave out begin and end wherever you can, you get into a different type of trouble.
This has happened to me lots. You can get in trouble when you insert a line into a place where no begin and end statement exist. You then end up scratching your head wondering what you did that broke your code. Begin-end-everywhere, even where not needed, is standard operating procedure for a lot of Delphi coders.
The only thing you should keep in mind about if-elseif-else is to keep the common cases up in your code before edge cases, so that the least possible conditions are evaluated.

Resources