www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Performance of default and enum parameters

reply jmh530 <john.michael.hall gmail.com> writes:
I'm a little curious on the impact of default and enum parameters 
on performance.

In the simple example below, I have foo and bar that return 
different results depending on a bool. Foo is a template with 
check as a template parameter and bar is a normal function. foo 
creates two different versions so it never needs to perform a 
run-time evaluation of check.

However, I was a little curious about bar. Obviously if check is 
passed as either true or false, then the if statement has to be 
run, but I wasn't sure what happens if check is not passed or if 
check is known at compile-time.

If check is not passed, I think it depends on how default 
function arguments work. I could imagine that it works in two 
ways: 1) if you call bar(x), then the compiler effectively 
re-writes it to bar(x, true), which would mean that the if 
statement must run, 2) the compiler creates functions int bar(int 
x) and int bar(int x, bool check), where the first has the check 
optimized away and the second is as normal, calling bar(x) means 
that the optimized one is called.

If check is known at compile-time, I would think that the 
compiler could optimize away the if statement because it is known 
at compile-time which part will be there or not.

int foo(bool check = true)(int x)
{
     static if (check)
         return x;
     else
         return x + 1;
}

int bar(int x, bool check = true)
{
     if (check)
         return x;
     else
         return x + 1;
}

enum bool val = false;

void main()
{
     import std.stdio : writeln;

     int x = 1;

     writeln(foo(x));        //two versions of foo
     writeln(foo!false(x));  //two versions of foo

     writeln(bar(x));        //does run-time if happen?
     writeln(bar(x, true));  //if statement at run-time
     writeln(bar(x, false)); //if statement at run-time

     writeln(foo!val(x));    //same as foo!false
     writeln(bar(x, val));   //does run-time if happen?
}
Feb 07
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Feb 07, 2018 at 11:04:29PM +0000, jmh530 via Digitalmars-d-learn wrote:
[...]
 If check is not passed, I think it depends on how default function
 arguments work. I could imagine that it works in two ways: 1) if you
 call bar(x), then the compiler effectively re-writes it to bar(x,
 true), which would mean that the if statement must run, 2) the
 compiler creates functions int bar(int x) and int bar(int x, bool
 check), where the first has the check optimized away and the second is
 as normal, calling bar(x) means that the optimized one is called.
Default arguments mean precisely that: if you don't specify that parameter, the compiler inserts the default argument for you. IOW: int bar(int x, bool check = true) { ... } bar(1); // rewritten as bar(1, true);
 If check is known at compile-time, I would think that the compiler
 could optimize away the if statement because it is known at
 compile-time which part will be there or not.
[...] Default arguments are a syntactic construct. They have little to do with what optimizations are done by the compiler. A function without default arguments can be optimized the same way as a function with default arguments. Whether or not something is optimized away is more dependent on the optimizer than on the syntax used to write the code. If you want to know for sure, check the assembly output, e.g., of ldc. The last time I checked, ldc is able to inline and optimize away even nested loops (up to a certain complexity), needless to say a simple boolean if-statement like your example code. This was in the context of certain proposed Phobos optimizations, and it was funny because we were getting strange benchmark results like 0ms for the entire function when compiled with ldc. Disassembling showed that what actually happened was that ldc determined that since the return value of the function was never used, the entire function call could be deleted completely. In another case, because the function arguments were known at compile-time, ldc executed the function at compile-time and substituted the function call with just loading the return value directly into a register, so the function was never actually called. Basically, if you use a compiler with an aggressive optimizer like ldc's, don't waste your time with micro-optimizations. The compiler will do it for you. Trying to hand-optimize your code can sometimes backfire -- the code can become too strange for the optimizer to understand, so it gives up and ends up *not* optimizing it at all. In this day and age, the only time you actually need to hand-optimize is when a profiler has identified real hotspots, and usually the fix is relatively simple. (And IME, the real hotspots are often NOT where I predict them to be. So optimizing stuff before I have real data from a profiler is usually a waste of my time.) T -- Public parking: euphemism for paid parking. -- Flora
Feb 07
parent jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 7 February 2018 at 23:18:53 UTC, H. S. Teoh wrote:
 [snip]
Appreciate the detailed reply. I had tried to look at the assembly with that tool recently discussed in the announce thread, but there was so much extra stuff reported that I threw up my hands.
Feb 07