From: Art on
hello everyone,

today i tested the speed of a delegate relaying to a function against
the speed of an an if, that checks the condition of a boolean value,
relaying to a function (the code is attached at the end of this post).
in my test it turned out, the delegate was twice as fast as checking
the boolean.

this lead me to the conclusion, that whenever object behavior depends
on values, known from time of construction, it is faster to compose an
object (compose of delegates and/or other objects) at construction
time, rather than checking condition(s) every time an object is used.
this goes with the constraint, that cost of construction by
composition must be smaller than the summarized cost of using the
object over its lifetime (cost of construction by composition must be
reasonable low).

does everyone concure? do i miss something?


regards,
arthur




here is the code:

public abstract class BaseClass
{
protected bool _Value;
public BaseClass(bool Value)
{
_Value=Value;
this.Initialize();
}
protected abstract void Initialize();
public abstract void Action();
protected void Action1() { }
protected void Action2() { }
}

public class DelegateClass : BaseClass
{
public DelegateClass(bool Value) : base(Value) { }
private DoDelegate _DoAction = null;
protected override void Initialize()
{
if (_Value)
_DoAction = new DoDelegate(this.Action1);
else
_DoAction = new DoDelegate(this.Action2);
}
public override void Action()
{
_DoAction();
}
private delegate void DoDelegate();
}

public class ConditionClass : BaseClass
{
public ConditionClass(bool Value) : base(Value) { }
protected override void Initialize()
{
}
public override void Action()
{
if (_Value)
this.Action1();
else
this.Action2();
}
}

class Program
{
static void Main(string[] args)
{
int iterations = 100000000;
Console.WriteLine(iterations);
bool value = true;
Console.WriteLine(value);

BaseClass action = new DelegateClass(value);
TimeSpan duration = Iterate(iterations, action);
Console.WriteLine("Delegate: "+duration);

action = new ConditionClass(value);
duration = Iterate(iterations, action);
Console.WriteLine("Condition: "+duration);

Console.WriteLine("Press ENTER to exit.");
Console.ReadLine();
}

private static TimeSpan Iterate(int iterations, BaseClass
action)
{
DateTime start = DateTime.Now;
for (int i = 0; i < iterations; i++)
{
action.Action();
}
DateTime end = DateTime.Now;
TimeSpan duration = end.Subtract(start);
return duration;
}
}
From: Peter Duniho on
Art wrote:
> hello everyone,
>
> today i tested the speed of a delegate relaying to a function against
> the speed of an an if, that checks the condition of a boolean value,
> relaying to a function (the code is attached at the end of this post).
> in my test it turned out, the delegate was twice as fast as checking
> the boolean.
>
> this lead me to the conclusion, that whenever object behavior depends
> on values, known from time of construction, it is faster to compose an
> object (compose of delegates and/or other objects) at construction
> time, rather than checking condition(s) every time an object is used.
> this goes with the constraint, that cost of construction by
> composition must be smaller than the summarized cost of using the
> object over its lifetime (cost of construction by composition must be
> reasonable low).
>
> does everyone concure? do i miss something?

One big issue with your benchmark code is that it does no "warm-up"
iterations to attempt to get things into a stable state (JIT compiled,
cache lines set, CPU pipelines filled including pre-fetch and branch
prediction, etc.). You also do only one trial per execution of the
program, rather than doing several and averaging the results after
discarding the outliers. So your observations may or may not be accurate.

That said, while I haven't tried the test myself, I see no obvious
reason that a delegate invocation _wouldn't_ be faster, since it's an
invariant condition while the boolean test is not (or at least, there's
no way for the compiler or CPU to know that it's not). And it stands to
reason that invariant conditions can be cached/predicted/etc. better
than variant conditions.

But, your analysis overlooks a few very important points:

� The difference in speed (if any) might be accounted for by
optimizations that the JIT compiler or even CPU is able to make in your
benchmark code, but which would not apply in a more complicated
scenario. The only true way to know for sure whether an optimization
will help is to test it in production code.

� Delegates cost memory, and use of memory can slow a program down.
So whether this approach nets a benefit will depend heavily on how many
delegates the approach winds up creating. Doing it in a static class or
in an instance of a class that is created only once or a few times won't
change the outcome, but doing it in a class that is instantiated large
number of times could produce a significant increase in memory usage,
which can disturb locality or even cause increased swap file usage, both
of which will _dramatically_ slow the program down.

� Whatever the difference in performance cost, it is highly unlikely
that it is significant when compared to the cost of the code being
controlled by the condition. Your benchmark is pretty much a worst-case
scenario, because the methods being called do nothing at all. A method
that does even something as simple as checking a boolean flag will
automatically take at least as long as the dispatch mechanism itself,
and of course most methods do things far more interesting than that.

I would expect in the typical case that the cost of dispatch winds up
being less than 10% of the total cost of calling the method, perhaps
even less than 1%, and of course that difference in cost is further
reduced in significant when the cost of the one method being dispatched
is considered in context of the entire program. Even if that method
accounts for 10% of the execution time of the program (which would be
very unusual), that means that the net effect on program execution time
would be less than 1% even under the generous assumption that the
dispatching difference can account for a 10% improvement in method
invocation time.

In other words, even if this is a valid observation, it's unlikely to
have any practical effect since the performance improvement is not large
enough to outweigh the question of which version is more maintainable.
Either _could_ be the more maintainable approach, depending on context,
and _that_ is the question that should drive the implementation here,
not the performance considerations.

Pete