C# out parameter performance
Asked Answered
A

6

27

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 successful and having an out parameter containing some additional data if it was successful.

Alcheringa answered 9/2, 2009 at 12:31 Comment(0)
A
40

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!

Adonis answered 9/2, 2009 at 12:58 Comment(12)
Intersting result - in my case the out parameter is just an int enum, nothing big though.Alcheringa
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.Nielsen
@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.Adonis
@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.Nielsen
+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.Sulfonation
@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.Nielsen
@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 :( )Melo
@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.Adonis
I think the NoInlining option is misplaced here - the JIT should inline the call; that would make the redundant copy much more obvious to the optimizer. However, removing the attribute and rerunning the test result in the same trend - the return value version is (much) slower. Using AggressiveInlining hardly helps; but it does affect performance in a slightly altered benchmark, so it does appear to work. IOW: even when inlined, the JIT compiler cannot detect redundant copies very well.Jonniejonny
@JonSkeet: One curious quirk of C# and vb.net is that a statement like someVariable = new structType(params); is often implemented by passing someVariable as an out parameter to the constructor; this is good for performance, but can sometimes cause interesting semantic quirks. Because out parameters are a C# rather than .net concept, a virtual method with an out parameter that gets overridden in a language other than C# need not do anything with that parameter. If a struct constructor passes this to such a method, C# considers it definitely assigned, but it may not get written.Mitman
Hi. I'm from the future. And in our time (2018, .NET 4.7.2), I ran this benchmark. I got the following: Out -> 982, Return -> 985 (compiled for x64, x86 is more like ~2700 return vs ~1550 out). Just putting this here for those who might be looking into this.Salpiglossis
+1 for "As normal, write the most readable code and test whether performance is already good enough before trying to optimise further."Sanderlin
N
5

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.

Nielsen answered 9/2, 2009 at 12:35 Comment(11)
I think I disagree strongly with the last statement... care to back it up? Writing a little test app...Adonis
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.Nielsen
Konrad: IIRC, on x86 machine code level, you return values with eaxAlcheringa
Jon: also look at this: 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!Nielsen
@DrJokepu: my point exactly; eax only holds 32 bits. You may fit a pointer or a number in there, but rarely a full object.Nielsen
@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.Adonis
@Jon - so does that mean that for large structs out parameters would be more performant?Melo
The last sentence of the clarification is the important difference IMO :)Adonis
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.Redman
@Augusto: to prevent this, memory for the return value is allocated on the caller site, not on the callee site.Nielsen
I got it from your link, pretty interesting indeed.Redman
S
5

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.

Schizo answered 9/2, 2009 at 12:56 Comment(0)
P
1

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.

Primalia answered 9/2, 2009 at 12:35 Comment(5)
It can be usefull in Try.. functions like Dictionary<>.TryGetValue and int.TryParse...Bordure
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.Alcheringa
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.Primalia
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.Schizo
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.Primalia
B
1

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.

Bordure answered 9/2, 2009 at 12:36 Comment(3)
I think you're thinking of a different language than c# :)Scurrility
@Giovanni - care to qualify? It sounds fine to me - the only minor point is that the compiler does the de-reference, not you...Schizo
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.Bordure
S
0

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.

Scurrility answered 9/2, 2009 at 12:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.