Chris Nicholson-Sauls wrote:
Lars Kyllingstad wrote:
I have some functions for which I want to find the nicest possible
combination of performance and usability. I have two suggestions as to
how they should be defined.
"Classic" style:
real myFunction(real arg, int someParam, inout real aReturnValue)
{
declare temporary variables;
do some calculations;
store a return value in aReturnValue;
return main return value;
}
The user-friendly way, where the function is encapsulated in a class:
class MyFunctionWorkspace
{
declare private temporary variables;
real anotherReturnValue;
this (int someParam)
{ ... }
real myFunction(real arg)
{
do some calculations;
store a return value in aReturnValue;
return main return value;
}
}
I'm sure a lot of people will disagree with me on this, but let me
first say why I think the last case is more user-friendly. For one
thing, the same class can be used over and over again with the same
parameter(s). Also, the user only has to retrieve aReturnValue if it
is needed. If there are many such "additional" inout parameters which
are seldom needed, it gets tedious to declare variables for them every
time the function is called. I could overload the function, but this
also has drawbacks if there are several inout parameters with the same
type.
My questions are:
- If I do like in the second example above, and reuse temporary
variables instead of allocating them every time the function is
called, could this way also give the best performance? (Yes, I know
this is bad form...)
...or, if not...
- If I (again in the second example) move the temporary variables
inside the function, so they are allocated on the stack instead of the
heap (?), will this improve or reduce performance?
I could write both types of code and test them against each other, but
I am planning to use the same style for several different functions in
several modules, and want to find the solution which is generally the
best one.
-Lars
If I understand right that your main concern is with parameters that are
used over and over and over again -- which I can empathize with -- you
could also look into function currying. Assuming you are using Phobos,
the module you want to look at is std.bind, usage of which is pretty
straightforward. Given a function:
real pow (real base, real exp);
You could emulate a square() function via std.bind like so:
square = bind(&pow, _0, 2.0);
square(42.0); // same as: pow(42.0, 2.0)
If you are using Tango, I'm honestly not sure off the top of my head
what the relevant module is, but you could always install Tangobos and
use std.bind just fine.
All that being said, I have no experience with currying functions with
inout parameters. If my understanding of how std.bind works its magic
is right, it should be fine. I believe it wraps the call up in a
structure, which means the actual parameter will be from a field of said
structure... which, actually, means it could also store state. That in
itself could be an interesting capability.
-- Chris Nicholson-Sauls
Most of the time I use Tango, but in this particular case I don't want
my code to depend on either library. Also I'm not sure whether the
std.bind functionality is even present in Tango. I could always write
my.own.bind, though.
Your solution is nice from a usability perspective, in that it reuses
function arguments -- possibly even inout ones. From a performance
perspective, however, it carries with it the overhead of an extra
function call, which I'm not sure I want.
-Lars