www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - write a function template specialisation that tests if an argument is

reply Cecil Ward <d cecilward.com> writes:
T myfunc(T)( T x, uint mask )
    if ( mask == 3 )
    {
    return fast_func( x, mask );
    }

but of course this doesn't work because mask is not known at 
compile-time. so I wondered if there is a way to do something 
like static if ( isKnownAtCompileTime( mask ) ) but that would 
not necessarily help me and probably isn't the right way.

Basically there is a fast path for certain known values of a 
(second in this case) argument where the compiler could produce 
superb trivial code or where I can work out a shortcut myself. 
for example myfunc( x, 0 ) == 0 and myfunc( x, -1 ) == x and 
various other good things, and for some values of mask the thing 
behaves like an AND operation so I want the compiler to just 
generate that.

The default slow path where the arg is unknown involves calling 
asm so the compiler cannot use its intelligence as it does not 
know the detailed semantics.

Also:

To add further complication: if both arguments of myfunc() are 
known at compile-time, then I definitely want to take an 
alternative path because then I can apply CTFE and calculate a 
compile-time result.
Aug 10 2018
next sibling parent Cecil Ward <d cecilward.com> writes:
On Saturday, 11 August 2018 at 05:17:51 UTC, Cecil Ward wrote:
 T myfunc(T)( T x, uint mask )
    if ( mask == 3 )
    {
    return fast_func( x, mask );
    }

 but of course this doesn't work because mask is not known at 
 compile-time.
Actually is there an opportunity for some kind of language enhancement there? I do not really know what I am talking about AT ALL but if the compiler could silently add an extra specialisation that gets generated at compile time, with constant folding and all the optimisations that follow from it, if a call with an appropriate constant argument is seen? But this is probably horrible because that kind of stuff is ph performed at a completely different point ?
Aug 10 2018
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 11 August 2018 at 05:17:51 UTC, Cecil Ward wrote:
 T myfunc(T)( T x, uint mask )
    if ( mask == 3 )
    {
    return fast_func( x, mask );
    }

 but of course this doesn't work because mask is not known at 
 compile-time. so I wondered if there is a way to do something 
 like static if ( isKnownAtCompileTime( mask ) ) but that would 
 not necessarily help me and probably isn't the right way.
You can create an overload where `mask` is passed as a template parameter: T myfunc(uint mask, T)(T x) { static if(mask == 3) { return fast_func(x, mask); } else { return func(x, mask); } } The same technique is used by `std.format.format` in the standard library to pass a format string that's known at compile time.
Aug 11 2018
parent reply Cecil Ward <d cecilward.com> writes:
On Saturday, 11 August 2018 at 18:11:15 UTC, Paul Backus wrote:
 On Saturday, 11 August 2018 at 05:17:51 UTC, Cecil Ward wrote:
 T myfunc(T)( T x, uint mask )
    if ( mask == 3 )
    {
    return fast_func( x, mask );
    }

 but of course this doesn't work because mask is not known at 
 compile-time. so I wondered if there is a way to do something 
 like static if ( isKnownAtCompileTime( mask ) ) but that would 
 not necessarily help me and probably isn't the right way.
You can create an overload where `mask` is passed as a template parameter: T myfunc(uint mask, T)(T x) { static if(mask == 3) { return fast_func(x, mask); } else { return func(x, mask); } } The same technique is used by `std.format.format` in the standard library to pass a format string that's known at compile time.
Paul, what would the calls look like? I am about to misunderstand things completely so here goes :-) It would be a bit kludgy having to switch from one calling syntax to another, putting the mask argument in the template parameters or in the normal position. Or have I misunderstood? And if the caller did not use the right call syntax variant then the optimisation would not happen. Thing is, as it is the details are nicely hidden and the caller does not even need to thing about the fact that an (eponymous) template is being used.
Aug 11 2018
parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 12 August 2018 at 00:15:37 UTC, Cecil Ward wrote:
 Paul, what would the calls look like?

 I am about to misunderstand things completely so here goes :-)

 It would be a bit kludgy having to switch from one calling 
 syntax to another, putting the mask argument in the template 
 parameters or in the normal position. Or have I misunderstood? 
 And if the caller did not use the right call syntax variant 
 then the optimisation would not happen. Thing is, as it is the 
 details are nicely hidden and the caller does not even need to 
 thing about the fact that an (eponymous) template is being used.
As far as I know, there's no way to *guarantee* the optimization and keep the normal function call syntax. Probably the best you can do is write the mask check as a regular if statement, put `pragma(inline, true)` in the function, and hope the optimizer is smart enough to get rid of the branch.
Aug 11 2018
parent reply Cecil Ward <d cecilward.com> writes:
On Sunday, 12 August 2018 at 00:55:50 UTC, Paul Backus wrote:
 On Sunday, 12 August 2018 at 00:15:37 UTC, Cecil Ward wrote:
 Paul, what would the calls look like?

 I am about to misunderstand things completely so here goes :-)

 It would be a bit kludgy having to switch from one calling 
 syntax to another, putting the mask argument in the template 
 parameters or in the normal position. Or have I misunderstood? 
 And if the caller did not use the right call syntax variant 
 then the optimisation would not happen. Thing is, as it is the 
 details are nicely hidden and the caller does not even need to 
 thing about the fact that an (eponymous) template is being 
 used.
As far as I know, there's no way to *guarantee* the optimization and keep the normal function call syntax. Probably the best you can do is write the mask check as a regular if statement, put `pragma(inline, true)` in the function, and hope the optimizer is smart enough to get rid of the branch.
I was thinking about reflection and powerful things like traits. Would a test to see if a static if compile do the trick ? You ask the question using traits : "does the following compile? : { static if ( mask == 3 ) { }; }" - any use?
Aug 11 2018
next sibling parent Cecil Ward <d cecilward.com> writes:
On Sunday, 12 August 2018 at 02:17:21 UTC, Cecil Ward wrote:
 On Sunday, 12 August 2018 at 00:55:50 UTC, Paul Backus wrote:
 On Sunday, 12 August 2018 at 00:15:37 UTC, Cecil Ward wrote:
 Paul, what would the calls look like?

 I am about to misunderstand things completely so here goes :-)

 It would be a bit kludgy having to switch from one calling 
 syntax to another, putting the mask argument in the template 
 parameters or in the normal position. Or have I 
 misunderstood? And if the caller did not use the right call 
 syntax variant then the optimisation would not happen. Thing 
 is, as it is the details are nicely hidden and the caller 
 does not even need to thing about the fact that an 
 (eponymous) template is being used.
As far as I know, there's no way to *guarantee* the optimization and keep the normal function call syntax. Probably the best you can do is write the mask check as a regular if statement, put `pragma(inline, true)` in the function, and hope the optimizer is smart enough to get rid of the branch.
I was thinking about reflection and powerful things like traits. Would a test to see if a static if compile do the trick ? You ask the question using traits : "does the following compile? : { static if ( mask == 3 ) { }; }" - any use?
I am out of my depth but I am also wondering about using mixin in some way, conditionally. And the kind of thing I am also thinking about is something like if this ( xxx ) compiles then xxx // put in the code under a conditional compilation if else pull in alternative code and all the ifs are compile time. so zero run time penalty which absolutely essential in my case because in fact the bodies are just single instructions and it is a choice between a cheaper instruction ( or no instruction at all ) and a 3-4 times more costly instruction, but an if statement is 50 times more expensive because of branch misprediction risk as well as the cost of the test itself
Aug 11 2018
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 12 August 2018 at 02:17:21 UTC, Cecil Ward wrote:
 I was thinking about reflection and powerful things like 
 traits. Would a test to see if a static if compile do the trick 
 ? You ask the question using traits : "does the following 
 compile? : { static if ( mask == 3 ) { }; }"  - any use?
When the function is being compiled, it has no idea where its arguments will be coming from. So your `__traits(compiles, ...)` test will always evaluate to false.
Aug 11 2018
prev sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 11 August 2018 at 05:17:51 UTC, Cecil Ward wrote:
 T myfunc(T)( T x, uint mask )
    if ( mask == 3 )
    {
    return fast_func( x, mask );
    }

 [...]
Is it the volcano pattern you are looking for? https://p0nce.github.io/d-idioms/#Is-this-available-at-compile-time-or-runtime?
Aug 12 2018
parent reply Cecil Ward <d cecilward.com> writes:
On Sunday, 12 August 2018 at 12:27:59 UTC, Alex wrote:
 On Saturday, 11 August 2018 at 05:17:51 UTC, Cecil Ward wrote:
 T myfunc(T)( T x, uint mask )
    if ( mask == 3 )
    {
    return fast_func( x, mask );
    }

 [...]
Is it the volcano pattern you are looking for? https://p0nce.github.io/d-idioms/#Is-this-available-at-compile-time-or-runtime?
Wow, now that _is_ clever. I think that is definitely a big part of it. Now somehow after having used a static if to select the known-at-compile-time case I then have to test the argument for particular values. So how to get the next step along the way?
Aug 13 2018
parent reply Cecil Ward <d cecilward.com> writes:
On Tuesday, 14 August 2018 at 02:53:01 UTC, Cecil Ward wrote:
 On Sunday, 12 August 2018 at 12:27:59 UTC, Alex wrote:
 On Saturday, 11 August 2018 at 05:17:51 UTC, Cecil Ward wrote:
 T myfunc(T)( T x, uint mask )
    if ( mask == 3 )
    {
    return fast_func( x, mask );
    }

 [...]
Is it the volcano pattern you are looking for? https://p0nce.github.io/d-idioms/#Is-this-available-at-compile-time-or-runtime?
Wow, now that _is_ clever. I think that is definitely a big part of it. Now somehow after having used a static if to select the known-at-compile-time case I then have to test the argument for particular values. So how to get the next step along the way?
Would it be ok to ask Walter maybe ? I am so far out of my depth here tho.
Aug 13 2018
parent Eric <noreply null.com> writes:
On Tuesday, 14 August 2018 at 03:01:11 UTC, Cecil Ward wrote:
 On Tuesday, 14 August 2018 at 02:53:01 UTC, Cecil Ward wrote:
 On Sunday, 12 August 2018 at 12:27:59 UTC, Alex wrote:
 On Saturday, 11 August 2018 at 05:17:51 UTC, Cecil Ward wrote:
 T myfunc(T)( T x, uint mask )
    if ( mask == 3 )
    {
    return fast_func( x, mask );
    }

 [...]
Is it the volcano pattern you are looking for? https://p0nce.github.io/d-idioms/#Is-this-available-at-compile-time-or-runtime?
Wow, now that _is_ clever. I think that is definitely a big part of it. Now somehow after having used a static if to select the known-at-compile-time case I then have to test the argument for particular values. So how to get the next step along the way?
Would it be ok to ask Walter maybe ? I am so far out of my depth here tho.
If you are that far, then it seems simple. You always call it as a template: T myfunc(uint mask, T)(T x) {} In myfunc you decide if umask is compile time or not. If not, then call a templated function with static if's for specific values, otherwise call the regular function.
Aug 14 2018