C#输出参数性能

C#中的参数是否具有我应该了解的性能影响? (像例外)

我的意思是,在一个循环中使用out参数的方法是一个好主意,每秒运行几百万次?

我知道它很难看,但我使用的方式与Int32.TryParse使用它们一样 – 返回一个bool来判断一些validation是否成功,并且如果成功则out包含一些额外数据的out参数。

我怀疑你会发现使用out参数会有任何明显的性能损失。 你必须以某种方式或其他方式将信息传回呼叫者 – 这只是一种不同的方式。 如果在方法中广泛使用out参数,您可能会发现存在一些损失,因为它可能意味着每次访问都需要额外的重定向级别。 但是,我不认为它会很重要。 正常情况下,编写最易读的代码并在尝试进一步优化之前测试性能是否已经足够好 。

编辑:其余部分是有效的。 它只与大值类型真正相关,通常应该避免使用:)

我不同意Konrad的断言,“所有类型的返回值> 32位的处理方式与机器级别的out参数相似或相同”。 这是一个小测试应用程序:

 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(); } } 

结果:

 Using return value: 11316 Using out parameter: 7461 

基本上通过使用out参数,我们将数据直接写入最终目标,而不是将其写入small方法的堆栈帧,然后将其复制回Main方法的堆栈帧。

尽管如此批评基准应用程序 - 我可能错过了一些东西!

这不是性能问题,而是之前出现的问题 – 您不能在C#4.0中使用它们 。

就个人而言,我倾向于在我的私有代码中使用相当多的参数(即在一个类中,有一个方法可以返回多个值而不使用单独的类型) – 但我倾向于在公共API上避免它们,除了bool Try{Something}(out result)模式。

没有性能影响。 从技术的角度来看, out与任何旧的论证基本相同。 虽然复制大量数据可能听起来似乎有道理(例如对于大型结构),但这实际上与返回值相同。

事实上,所有类型> 32位的返回值的处理方式与机器级别的 out参数类似。

请注意,最后一条语句并不建议在.NET中返回value == out参数。 Jon的基准测试显示,这显然(并且令人遗憾)并非如此。 实际上,为了使它相同,在C ++编译器中使用了命名返回值优化 。 在JIT的未来版本中可能会做类似的事情以提高返回大型结构的性能(但是,由于.NET中的大型结构非常少见,因此这可能是不必要的优化)。

但是 ,(并且由于我对x86程序集的知识非常有限),从函数调用返回对象通常需要在调用站点分配足够的空间,推送堆栈上的地址并通过将返回值复制到其中来填充它。 这与out完全相同,只是省略了值的不必要的临时副本,因为可以直接访问目标内存位置。

避免输出参数的主要原因是代码可读性,而不是性能。

对于值类型,无论如何都没有真正的区别(它们总是复制),对于引用类型,它与传递ref基本相同。

十分之九,你最好创建自己的哑记录类,而不是使用out参数 – 这在以后返回代码时更易于阅读和理解。

输出参数由ref传递。 所以只有指针在堆栈上传递。

如果您的值类型很大,则副本较少,但是您必须取消引用每个变量使用的指针。

使用out参数不会影响性能。 Out参数基本上是一个引用参数,因此调用者和被调用者都指向同一块内存。