tags:

views:

2270

answers:

6

Do out parameters in C# have any performance implications I should know about? (Like exceptions)

I mean, is it a good idea to have a method with an out parameter in a loop that will run a couple of million times a second?

I know it's ugly but I am using it the same way as Int32.TryParse is using them - returning a bool to tell if some validation was succesful and having and out parameter containing some additional data if it was succesful.

+5  A: 

There are no performance implications. out is basically the same as any old argument passing, from a technical point of view. While it might sound plausible that huge amounds of data are copied (e.g. for large structs), this is actually the same as for return values.

In fact, return values for all types > 32 bit are handled similar to out arguments on the machine level anyway.

Please note that the last statement doesn't suggest that returning a value == out parameter in .NET. Jon's benchmark shows that this is obviously (and regrettably) not the case. In fact, to make it identical, named return value optimization is employed in C++ compilers. Something similar could potentially be done in future versions of the JIT to improve performance of returning large structures (however, since large structures are quite rare in .NET, this might be an unnecessary optimization).

However, (and with my very limited knowledge of x86 assembly), returning objects from function calls generally entails allocating sufficient space at the call site, pushing the address on the stack and filling it by copying the return value into it. This is basically the same that out does, only omitting an unnecessary temporary copy of the value since the target memory location can be accessed directly.

Konrad Rudolph
I think I disagree strongly with the last statement... care to back it up? Writing a little test app...
Jon Skeet
Jon: I'm not sure what you mean. On machine code level (in X86 assembly) you return values by putting them on the stack, same as parameters. This is all that I meant. I wasn't alluding to any higher-level equivalence in CIL.
Konrad Rudolph
Konrad: IIRC, on x86 machine code level, you return values with eax
DrJokepu
Jon: also look at this: http://blogs.msdn.com/slippman/archive/2004/02/03/66739.aspx – I know that RVO is not a .NET concept but I think that the transformation mentioned there – from return value to out parameter – happens virtually everywhere. Also compare COM usage of the return value!
Konrad Rudolph
@DrJokepu: my point exactly; eax only holds 32 bits. You may fit a pointer or a number in there, but rarely a full object.
Konrad Rudolph
@Konrad: Look at my benchmark. Returning a value directly means having it on the original stack, then copying it to the caller. Using an out parameter avoids that - you write directly to the memory which wants it, at the cost of a bit of redirection.
Jon Skeet
@Jon - so does that mean that for large structs out parameters would be more performant?
configurator
@configurator: Yes. See the benchmark in my answer.
Jon Skeet
The last sentence of the clarification is the important difference IMO :)
Jon Skeet
I see what you mean, but won't the unwinding of the stack kill the reference address that is returned on %eax as optimization? Probably that it's off-topic in this question anyway.
Augusto Radtke
@Augusto: to prevent this, memory for the return value is allocated on the *caller* site, not on the callee site.
Konrad Rudolph
I got it from your link, pretty interesting indeed.
Augusto Radtke
+1  A: 

The main reason for avoiding out parameters is code readability, rather than performance.

For value types there's no real difference anyway (they always copy) and for reference types it's basically the same as passing by ref.

Nine times out of ten you're better off creating your own dumb record class, rather than using an out parameter - this is simpler to read and understand when you return to the code later.

Keith
Care to explain why?
SDX2000
It can be usefull in Try.. functions like Dictionary<>.TryGetValue and int.TryParse...
Think Before Coding
Yeah I don't quite like the out pattern either exactly for the same reasons but in this case it is actually more readable than using some container class.
DrJokepu
I'm not overly fond of the TryParse pattern for that reason, but there's always some exceptions. In your own code it's usually clearer not to use them, and there's no performance difference.
Keith
For value-types, you avoid a *second* copy, since the method writes directly into the space pointed at by the caller. For large structs this can be a saving.
Marc Gravell
@Marc: Exactly :)
Jon Skeet
Thanks @Marc and @Jon - so out params are actually quicker (+1 to both your answers)! You learn new stuff all the time here ;-) I still reckon that 90% of the time you're better off with a record class for readability though.
Keith
+1  A: 

Out parameters are passed by ref. So only a pointer passed on the stack.

If your value type is large, there is less copy, but then you have to dereference the pointer on each variable use.

Think Before Coding
I think you're thinking of a different language than c# :)
Giovanni Galbo
@Giovanni - care to qualify? It sounds fine to me - the only minor point is that the **compiler** does the de-reference, not you...
Marc Gravell
No, the value is not copied on the stack, so when the value is needed for reading (after being initialized... ok mutable variables are not recommended), you must get the value from the reference.
Think Before Coding
Yep, and actually it's not even the C# compiler but the JIT compiler..
Think Before Coding
+1  A: 

Using an out parameter does not hurt performance. An Out parameter is basically a reference parameter, so both the caller and the callee point to the same piece of memory.

Giovanni Galbo
+4  A: 

Not a performance issue, but something that came up earlier - you can't use them with variance in C# 4.0.

Personally, I tend to use out parameters a fair amount in my private code (i.e. inside a class, having a method that returns multiple values without using a separate type) - but I tend to avoid them on the public API, except for the bool Try{Something}(out result) pattern.

Marc Gravell
+10  A: 

I doubt that you'll find any significant performance penalty to using an out parameter. You've got to get information back to the caller somehow or other - out is just a different way of doing it. You may find there's some penalty if you use the out parameter extensively within the method, as it may well mean an extra level of redirection for each access. However, I wouldn't expect it to be significant. As normal, write the most readable code and test whether performance is already good enough before trying to optimise further.

EDIT: The rest of this is an aside, effectively. It's only really relevant for large value types, which should usually be avoided anyway :)

I disagree with Konrad's assertion about "return values for all types > 32 bit are handled similar or identical to out arguments on the machine level anyway" though. Here's a little test app:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

struct BigStruct
{
    public Guid guid1, guid2, guid3, guid4;
    public decimal dec1, dec2, dec3, dec4;
}

class Test
{
    const int Iterations = 100000000;

    static void Main()
    {
        decimal total = 0m;
        // JIT first
        ReturnValue();
        BigStruct tmp;
        OutParameter(out tmp);

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            BigStruct bs = ReturnValue();
            total += bs.dec1;
        }
        sw.Stop();
        Console.WriteLine("Using return value: {0}",
                          sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            BigStruct bs;
            OutParameter(out bs);
            total += bs.dec1;
        }
        Console.WriteLine("Using out parameter: {0}",
                          sw.ElapsedMilliseconds);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static BigStruct ReturnValue()
    {
        return new BigStruct();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void OutParameter(out BigStruct x)
    {
        x = new BigStruct();
    }
}

Results:

Using return value: 11316
Using out parameter: 7461

Basically by using an out parameter we're writing the data directly to the final destination, rather than writing it to the small method's stack frame and then copying it back into the Main method's stack frame.

Feel free to criticise the benchmark app though - I may have missed something!

Jon Skeet
Intersting result - in my case the out parameter is just an int enum, nothing big though.
DrJokepu
Jon: please consider my answer to your remark. Your code is actually rather nice because it reveals a pitiful lack of optimization in returning values but it's not what I meant with my statement.
Konrad Rudolph
@Konrad: Then I suggest you revise your statement. A C# out parameter is clearly *not* treated in the same way as a C# return value at the machine level in .NET.
Jon Skeet
@Jon: yes, I've added a clarification. Notice that your benchmark is exactly the reason why I originally wrote “similar or identical”: I wasn't sure whether the JIT performed the necessary optimization to omit the useless object copy.
Konrad Rudolph
+1 - Very nice work. This one must have appealed to your curiosity. I'd have guessed, like Konrad, that out params and return values would be handled on the stack. After thinking about it and seeing your example, though, it certainly *does* make sense to write results directly to the heap.
Mark Brittingham
@Mark: I doubt the heap is involved here (since these are all local variables). `out` will pass around a pointer to a stack location instead.
Konrad Rudolph
@Konrad: Thanks for the clarification, it helps a lot.
Jon Skeet
@Jon: I think there could be a hidden initialization for the variable in one of the loops. Try using BigStruct bs = new BigStruct(); - does that alter the results (I'd try but my laptop is broken :( )
configurator
@configurator: Yes, that makes the "out" version a bit slower (but still faster than the return) - but why would I want to do that in the first place? The value isn't being used.
Jon Skeet