具有单个对象字段的结构如何能比原始对象更快?

我有一个struct保存单个object字段,使对象的工作更容易。 我想测试性能(我预计有些降级),但是我得到了非常令人惊讶的结果。 带有struct的版本实际上更快:

无框:8.08秒

带框:7.76秒

这怎么可能?

以下是重现结果的完整测试代码。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication68
{
    partial class Program
    {
        private const int Iterations = 100000000;

        static void Main(string[] args)
        {
            // Force JIT compilation.

            TimeWithoutBox(new MyObject());
            TimeWithoutBox(7);
            TimeBox(new MyObject());
            TimeBox(7);

            // The tests.

            var withoutBox = new TimeSpan();
            var box = new TimeSpan();

            for (int i = 0; i < 10; i++)
            {
                withoutBox += TimeWithoutBox(new MyObject());
                withoutBox += TimeWithoutBox(7);
                box += TimeBox(new MyObject());
                box += TimeBox(7);
            }

            Console.WriteLine("Without box: " + withoutBox);
            Console.WriteLine("With box: " + box);

            Console.ReadLine();
        }

        private static TimeSpan TimeBox(object value)
        {
            var box = new MyBox(value);

            var stopwatch = Stopwatch.StartNew();

            for (int i = 0; i < Iterations; i++)
            {
                TestBox(box);
            }

            return stopwatch.Elapsed;
        }

        private static TimeSpan TimeWithoutBox(object value)
        {
            var stopwatch = Stopwatch.StartNew();

            for (int i = 0; i < Iterations; i++)
            {
                TestWithoutBox(value);
            }

            return stopwatch.Elapsed;
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void TestBox(MyBox box)
        {
            if (box.IsDouble)
                TakeDouble((double)box.Value);
            else if (box.IsObject)
                TakeObject((MyObject)box.Value);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void TestWithoutBox(object box)
        {
            if (box.GetType() == typeof(double))
                TakeDouble((double)box);
            else if (box.GetType() == typeof(MyObject))
                TakeObject((MyObject)box);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void TakeDouble(double value)
        {
            // Empty method to force consuming the cast.
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void TakeObject(MyObject value)
        {
            // Empty method to force consuming the cast.
        }
    }

    struct MyBox
    {
        private readonly object _value;

        public object Value
        {
            get { return _value; }
        }

        public MyBox(object value)
        {
            _value = value;
        }

        public bool IsDouble
        {
            get { return _value.GetType() == typeof(double); }
        }

        public bool IsObject
        {
            get { return _value.GetType() == typeof(MyObject); }
        }
    }

    class MyObject
    {
    }
}

编辑:

我已经将IsDoubleIsObject测试更改为与其他测试具有相同的语句。 我重新执行了应用程序,结果时间完全相同。

EDIT2:

此代码使用发布版本在没有附加调试器32位编译测试; .NET 4.5和Visual Studio 2012.针对64位编译提供了截然不同的结果; 在我的机器上:

无框:8.23秒

带框:16.99秒


我复制了确切的代码,在没有调试器的情况下运行它(两者都很重要!),并在x64上运行。 结果:

Without box: 00:00:07.9650541
With box: 00:00:16.0958162

将测试更改为:

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void TestBox(MyBox box)
    {
        if (box.Value.GetType() == typeof(double))
            TakeDouble((double)box.Value);
        else if (box.Value.GetType() == typeof(MyObject))
            TakeObject((MyObject)box.Value);
    }

使运行时间几乎相等:

Without box: 00:00:07.9488281
With box: 00:00:08.6084029

为什么? 因为JIT决定不内联IsDouble和手动内联帮助。 这很奇怪,因为它是一个很小的功能。 第13行的call是这个电话。

在这里输入图像描述

现在为什么仍然存在一些性能差异? .NET JIT不是那里最好的编译器......可能有些指令有点不同。 您可以通过比较两个版本的反汇编来找出答案。 我没有时间这样做,因为我预计这种差异是相当无关紧要的。

我希望一个C编译器来解决这个问题。 结构应该像它包含的单个object成员一样行为。 小方法应该内联。 今天的编译器技术绝对是可行的。 我们希望下一代JIT和NGEN可以做到这一点。 目前正在开发新的JIT(RyuJIT),他们正在将VC后端的优化转移到NGEN(最近宣布)。

链接地址: http://www.djcxy.com/p/75927.html

上一篇: How can a struct with a single object field be faster than a raw object?

下一篇: How to localize Firefox SDK Add