37

参考以下SE 答案

写的时候

A = A ?? B;

它与

if( null != A )
    A = A;
else
    A = B;

这是否意味着

if( null == A ) A = B;

会是首选,性能明智?

或者我可以假设当??符号中存在同一个对象时编译器会优化代码?

4

5 回答 5

21

不要担心性能,它可以忽略不计。

如果您对此感到好奇,请编写一些代码来测试使用Stopwatch并查看性能。我怀疑您需要进行几百万次迭代才能开始看到差异。

您也永远不能假设事物的实现,它们将来可能会发生变化-使您的假设无效。

我的假设是性能差异可能非常非常小。我个人会选择 null 合并运算符以提高可读性,它很好而且很简洁,并且很好地传达了这一点。我有时会这样做以进行延迟加载检查:

_lazyItem = _lazyItem ?? new LazyItem();
于 2012-07-10T11:49:37.757 回答
4

尽管 for 的性能??可以忽略不计,但副作用有时可能不可忽略。考虑以下程序。

using System;
using System.Diagnostics;
using System.Threading;

namespace TestProject
{
    class Program
    {
        private string str = "xxxxxxxxxxxxxxx";
        public string Str
        {
            get
            {
                return str;
            }
            set
            {
                if (str != value)
                {
                    str = value;
                }
                // Do some work which take 1 second
                Thread.Sleep(1000);
            }
        }

        static void Main(string[] args)
        {
            var p = new Program();

            var iterations = 10;

            var sw = new Stopwatch();
            for (int i = 0; i < iterations; i++)
            {
                if (i == 1) sw.Start();
                if (p.Str == null)
                {
                    p.Str = "yyyy";
                }
            }
            sw.Stop();
            var first = sw.Elapsed;

            sw.Reset();
            for (int i = 0; i < iterations; i++)
            {
                if (i == 1) sw.Start();
                p.Str = p.Str ?? "yyyy";
            }
            sw.Stop();
            var second = sw.Elapsed;

            Console.WriteLine(first);
            Console.WriteLine(second);

            Console.Write("Ratio: ");
            Console.WriteLine(second.TotalMilliseconds / first.TotalMilliseconds);
            Console.ReadLine();
        }

    }
}

在我的电脑上运行结果。

00:00:00.0000015
00:00:08.9995480
Ratio: 5999698.66666667

因为使用 有一个额外的分配??,并且有时可能无法保证分配的性能。这可能会导致性能问题。

我宁愿使用if( null == A ) A = B;而不是A = A ?? B;.

于 2017-07-13T07:18:58.223 回答
3

我的建议是检查 IL(中间语言)并比较不同的结果。然后,您可以准确地看到每个归结为什么,并决定什么是更优化的。但正如亚当在他的评论中所说,你最有可能最好关注可读性/可维护性,而不是这么小的东西的性能。

编辑:您可以使用 Visual Studio 附带的 ILDASM.exe 查看 IL 并打开已编译的程序集。

于 2012-07-10T11:50:57.033 回答
3

我刚刚在 C# 中尝试过这个 - 非常快,所以我的方法可能有错误。我使用以下代码并确定第二种方法比第一种方法花费了大约 1.75 倍。
@Lockszmith:在下面的编辑之后,比率是 1.115,有利于第一次实现

即使他们花费相同的时间,我个人也会使用内置的语言结构,因为它更清楚地向任何可能具有更多内置优化的未来编译器表达了您的意图。

@Lockszmith:我编辑了代码以反映评论中的建议

var A = new object();
var B = new object();

var iterations = 1000000000;

var sw = new Stopwatch();
for (int i = 0; i < iterations; i++)
{   
    if( i == 1 ) sw.Start();
    if (A == null)
    {
        A = B;
    }
}
sw.Stop();
var first = sw.Elapsed;

sw.Reset();
for (int i = 0; i < iterations; i++)
{
    if( i == 1 ) sw.Start();
    A = A ?? B;
}
sw.Stop();
var second = sw.Elapsed;

first.Dump();
second.Dump();

(first.TotalMilliseconds / second.TotalMilliseconds).Dump("Ratio");
于 2012-07-10T11:59:17.637 回答
3

是,有一点不同。

使用Visual Studio 2017 15.9.8定位.NET Framework 4.6.1。考虑下面的示例。

static void Main(string[] args)
{
    // Make sure our null value is not optimized away by the compiler!
    var s = args.Length > 100 ? args[100] : null;
    var foo = string.Empty;
    var bar = string.Empty;

    foo = s ?? "foo";
    bar = s != null ? s : "baz";

    // Do not optimize away our stuff above!
    Console.WriteLine($"{foo} {bar}");
}

使用ILDasm它变得很明显,编译器不会平等地对待这些语句。

?? 运营商

IL_001c:  dup
IL_001d:  brtrue.s   IL_0025
IL_001f:  pop
IL_0020:  ldstr      "foo"
IL_0025:  ldloc.0

有条件的空检查

IL_0026:  brtrue.s   IL_002f
IL_0028:  ldstr      "baz"
IL_002d:  br.s       IL_0030
IL_002f:  ldloc.0

显然,??运算符意味着堆栈值的重复(应该是s变量吧?)。我进行了一个简单的测试(多次),以了解两者中哪一个更快。在这台特定机器上string运行,我得到了这些平均数字:

?? operator took:           583 ms
null-check condition took: 1045 ms

基准示例代码:

static void Main(string[] args)
{
    const int loopCount = 1000000000;
    var s = args.Length > 1 ? args[1] : null; // Compiler knows 's' can be null
    int sum = 0;

    var watch = new System.Diagnostics.Stopwatch();
    watch.Start();

    for (int i = 0; i < loopCount; i++)
    {
        sum += (s ?? "o").Length;
    }

    watch.Stop();

    Console.WriteLine($"?? operator took {watch.ElapsedMilliseconds} ms");

    sum = 0;

    watch.Restart();

    for (int i = 0; i < loopCount; i++)
    {
        sum += (s != null ? s : "o").Length;
    }

    watch.Stop();

    Console.WriteLine($"null-check condition took {watch.ElapsedMilliseconds} ms");
}

所以答案是肯定的,有区别。

PS。StackOverflow 应该自动警告在同一个句子中提到“性能”和“可忽略不计”的帖子。只有原始发布者才能确定时间单位是否可以忽略不计。

于 2019-03-18T16:27:20.697 回答