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.

b w
  • 4,553
  • 4
  • 31
  • 36
Tamas Czinege
  • 118,853
  • 40
  • 150
  • 176

6 Answers6

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!

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Intersting result - in my case the out parameter is just an int enum, nothing big though. – Tamas Czinege Feb 09 '09 at 13:06
  • 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 Feb 09 '09 at 13:07
  • 1
    @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 Feb 09 '09 at 13:11
  • @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 Feb 09 '09 at 13:16
  • +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 Feb 09 '09 at 13:18
  • @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 Feb 09 '09 at 13:22
  • @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 Feb 09 '09 at 14:00
  • @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 Feb 09 '09 at 14:09
  • 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. – Eamon Nerbonne Feb 04 '13 at 14:33
  • @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. – supercat Feb 11 '13 at 21:48
  • 3
    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. – Mike Aug 20 '18 at 21:22
  • +1 for "As normal, **write the most readable code** and test whether performance is already good enough **before trying to optimise further**." – Thomas Aug 03 '21 at 18:35
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.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • I think I disagree strongly with the last statement... care to back it up? Writing a little test app... – Jon Skeet Feb 09 '09 at 12:49
  • 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 Feb 09 '09 at 12:59
  • Konrad: IIRC, on x86 machine code level, you return values with eax – Tamas Czinege Feb 09 '09 at 13:00
  • 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 Feb 09 '09 at 13:02
  • @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 Feb 09 '09 at 13:04
  • @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 Feb 09 '09 at 13:06
  • @Jon - so does that mean that for large structs out parameters would be more performant? – configurator Feb 09 '09 at 13:08
  • The last sentence of the clarification is the important difference IMO :) – Jon Skeet Feb 09 '09 at 13:30
  • 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. – Edwin Jarvis Feb 09 '09 at 13:35
  • @Augusto: to prevent this, memory for the return value is allocated on the *caller* site, not on the callee site. – Konrad Rudolph Feb 09 '09 at 14:19
  • I got it from your link, pretty interesting indeed. – Edwin Jarvis Feb 09 '09 at 14:51
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.

Community
  • 1
  • 1
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
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.

Keith
  • 150,284
  • 78
  • 298
  • 434
  • It can be usefull in Try.. functions like Dictionary<>.TryGetValue and int.TryParse... – thinkbeforecoding Feb 09 '09 at 12:37
  • 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. – Tamas Czinege Feb 09 '09 at 12:43
  • 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 Feb 09 '09 at 12:49
  • 3
    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 Feb 09 '09 at 12:52
  • 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 Feb 09 '09 at 21:20
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.

thinkbeforecoding
  • 6,668
  • 1
  • 29
  • 31
  • I think you're thinking of a different language than c# :) – Giovanni Galbo Feb 09 '09 at 12:39
  • @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 Feb 09 '09 at 12:53
  • 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. – thinkbeforecoding Feb 09 '09 at 12:53
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.

Giovanni Galbo
  • 12,963
  • 13
  • 59
  • 78