Jump to page: 1 2 3
Thread overview
Some performance questions
Feb 02, 2009
Lars Kyllingstad
Feb 02, 2009
Lars Kyllingstad
Feb 02, 2009
Lars Kyllingstad
Feb 02, 2009
grauzone
Feb 02, 2009
grauzone
Feb 02, 2009
grauzone
Feb 02, 2009
Lars Kyllingstad
Feb 03, 2009
bearophile
Feb 02, 2009
Lars Kyllingstad
Feb 02, 2009
Daniel Keep
Feb 02, 2009
Lars Kyllingstad
Feb 03, 2009
Lars Kyllingstad
February 02, 2009
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
February 02, 2009
Lars Kyllingstad wrote:
>         real anotherReturnValue;

Correction:
    real aReturnValue;
February 02, 2009
On Mon, Feb 2, 2009 at 8:31 AM, Lars Kyllingstad <public@kyllingen.nospamnet> 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.

Any gains you get from skipping the initial calculations will be swiftly cut down by the cost of heap allocation and cache misses, if you allocate this object several times.

A much better way to get the usability of the latter with the better performance of the former is to use a struct instead of a class.  I highly doubt you'll be needing to inherit these "operation objects" anyway.  The struct will be allocated on the stack, and you still get all the usability.
February 02, 2009
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
February 02, 2009
Jarrett Billingsley wrote:
> On Mon, Feb 2, 2009 at 8:31 AM, Lars Kyllingstad
> <public@kyllingen.nospamnet> 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.
> 
> Any gains you get from skipping the initial calculations will be
> swiftly cut down by the cost of heap allocation and cache misses, if
> you allocate this object several times.

OK. But if the object is allocated once (or seldomly, at least), and I allocate any working variables on the stack, then the second case may not be half bad?

> A much better way to get the usability of the latter with the better
> performance of the former is to use a struct instead of a class.  I
> highly doubt you'll be needing to inherit these "operation objects"
> anyway.  The struct will be allocated on the stack, and you still get
> all the usability.

Thanks, I hadn't even thought of that! :) This could certainly be a solution. There are two problems, however:

1) In D1, structs don't have constructors, which could again make the initial parameter setting a tedious task. But this is not a big problem, as I could just define a static opCall for each struct as a kind of constructor.

2) Bigger problem: I was kinda hoping that all the functions could implement a common interface, so I can use them in generic algorithms. This could possibly be done with structs using templates, but plain old interfaces would be a cleaner solution.


-Lars
February 02, 2009
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
February 02, 2009

Lars Kyllingstad wrote:
> [snip]
> From a performance
> perspective, however, it carries with it the overhead of an extra
> function call, which I'm not sure I want.
> 
> -Lars

You're worried about a second function call which could potentially be inlined, yet you're seemingly not worried about the overhead of virtual calls or heap allocations...

Allow me to quote Donald Knuth:

> We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

Unless you're doing something where you *know* you're going to need every last cycle, just go with whichever design works best.  Your response to Jarrett implies that you've already got a design in mind, and are just fishing for a magic "make it go faster button."

Believe me, if Walter had invented such a thing, he wouldn't be wasting his time putting up with us; he'd be too busy smoking $100 bills from the comfort of his SPACE FORTRESS.  :D

In any case, I'm willing to bet that if there *are* inefficiencies you're not going to know exactly where until you've written the code, anyway.  :P

If classes work, and make for an elegant design, go for it.

  -- Daniel
February 02, 2009
Jarrett Billingsley wrote:
> On Mon, Feb 2, 2009 at 8:31 AM, Lars Kyllingstad
> <public@kyllingen.nospamnet> 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.
> 
> Any gains you get from skipping the initial calculations will be
> swiftly cut down by the cost of heap allocation and cache misses, if
> you allocate this object several times.
> 
> A much better way to get the usability of the latter with the better
> performance of the former is to use a struct instead of a class.  I
> highly doubt you'll be needing to inherit these "operation objects"
> anyway.  The struct will be allocated on the stack, and you still get
> all the usability.

Why not use scope to allocate the class on the stack?
For everything else, I agree with Donald Knuth (if he really said that...)
February 02, 2009
On Mon, Feb 2, 2009 at 1:27 PM, grauzone <none@example.net> wrote:
> Why not use scope to allocate the class on the stack?
> For everything else, I agree with Donald Knuth (if he really said that...)

That's fine too, and would fit in with his needs to implement interfaces.  But again, if he's worried about caching some parameters but not worried about the overhead of virtual calls.. something's off.
February 02, 2009
Jarrett Billingsley wrote:
> On Mon, Feb 2, 2009 at 1:27 PM, grauzone <none@example.net> wrote:
>> Why not use scope to allocate the class on the stack?
>> For everything else, I agree with Donald Knuth (if he really said that...)
> 
> That's fine too, and would fit in with his needs to implement
> interfaces.  But again, if he's worried about caching some parameters
> but not worried about the overhead of virtual calls.. something's off.

Or he's caching some very big/complex parameters in the code he's actually writing... maybe. That said: do we have any assurance that, were the functor class tagged as 'final', the call would cease to be virtual?  If so, then the only extra cost on the call is that of the hidden "this" sitting in ESI.  I still don't care for the memory allocation involved, personally, but if these are long-lived functors that may not be a major problem.  (Ie, if he calls foo(?,X) a million times, the cost of allocating one object is amortized into nearly nothing.)

-- Chris Nicholson-Sauls
« First   ‹ Prev
1 2 3