C# 为什么.NET Core 2.0的性能比.NET Framework 4.6.1差

C# 为什么.NET Core 2.0的性能比.NET Framework 4.6.1差,c#,.net-core,C#,.net Core,我写了一个程序,创建了4个线程,每个线程将20000个数字从低到高排序50次。我已经在.NETCore2.0和.NETFramework4.6.1上运行了多次此测试。在这个测试中,.NET框架总是优于.NET核心 设置 .NET Core处于发布模式并已发布 Windows 10,i7双核,4线程(超线程) 以下代码已用于对这两个框架进行基准测试 static void Main() { const int amountParallel = 4; va

我写了一个程序,创建了4个线程,每个线程将20000个数字从低到高排序50次。我已经在.NETCore2.0和.NETFramework4.6.1上运行了多次此测试。在这个测试中,.NET框架总是优于.NET核心

设置

  • .NET Core处于发布模式并已发布
  • Windows 10,i7双核,4线程(超线程)
以下代码已用于对这两个框架进行基准测试

static void Main()
    {
        const int amountParallel = 4;
        var globalStopwatch = new Stopwatch();

        globalStopwatch.Start();

        var tasks = new Task<double[]>[4];

        for (int i = 0; i < amountParallel; i++)
        {
            tasks[i] = Start();
        }

        Task.WaitAll(tasks);

        globalStopwatch.Stop();

        Console.WriteLine("Averages: {0}ms", tasks.SelectMany(r => r.Result).Average(x => x));
        Console.WriteLine("Time completed: {0}", globalStopwatch.Elapsed.TotalMilliseconds);
    }

    private static Task<double[]> Start()
    {
        return Task.Factory.StartNew(() =>
        {
            var numbersToSort = new int[20000];

            var globalStopwatch = new Stopwatch();
            var individualStopwatch = new Stopwatch();
            var stopwatchTimes = new double[50];
            int temp;

            globalStopwatch.Start();

            for (int i = 0; 50 > i; i++)
            {
                Console.WriteLine("Running task: {0}", i);
                numbersToSort = Enumerable.Range(0, 20000).Reverse().ToArray();
                individualStopwatch.Start();

                for (int indexNumberArray = 0; numbersToSort.Length > indexNumberArray; indexNumberArray++)
                {
                    for (int sort = 0; numbersToSort.Length - 1 > sort; sort++)
                    {
                        if (numbersToSort[sort] > numbersToSort[sort + 1])
                        {
                            temp = numbersToSort[sort + 1];
                            numbersToSort[sort + 1] = numbersToSort[sort];
                            numbersToSort[sort] = temp;
                        }
                    }
                }

                individualStopwatch.Stop();

                Console.WriteLine("Task {0} completed, took: {1}ms", i, Math.Round(individualStopwatch.Elapsed.TotalMilliseconds));

                stopwatchTimes[i] = individualStopwatch.Elapsed.TotalMilliseconds;

                individualStopwatch.Reset();
            }

            globalStopwatch.Stop();

            Console.WriteLine("Total time: {0}s", Math.Round(globalStopwatch.Elapsed.TotalSeconds, 2));
            Console.WriteLine("Average: {0}ms", Math.Round(stopwatchTimes.Average(time => time)));

            return stopwatchTimes;
        }, TaskCreationOptions.LongRunning);
    }
static void Main()
{
const int amountParallel=4;
var globalStopwatch=新秒表();
globalStopwatch.Start();
var tasks=新任务[4];
对于(int i=0;ir.Result.Average(x=>x));
WriteLine(“完成时间:{0}”,globalStopwatch.appeased.totalmillizes);
}
私有静态任务开始()
{
返回Task.Factory.StartNew(()=>
{
var numbersToSort=新整数[20000];
var globalStopwatch=新秒表();
var individualStopwatch=新秒表();
var秒表时间=新的双精度[50];
内部温度;
globalStopwatch.Start();
对于(int i=0;50>i;i++)
{
WriteLine(“正在运行的任务:{0}”,i);
numbersToSort=Enumerable.Range(020000).Reverse().ToArray();
单个秒表。开始();
对于(int indexnumberraray=0;numbersToSort.Length>indexnumberraray;indexnumberraray++)
{
for(int sort=0;numbersToSort.Length-1>sort;sort++)
{
if(numbersToSort[sort]>numbersToSort[sort+1])
{
temp=numbersToSort[sort+1];
numbersToSort[sort+1]=numbersToSort[sort];
numbersToSort[排序]=温度;
}
}
}
个人秒表。停止();
WriteLine(“任务{0}完成,耗时:{1}毫秒”,i,Math.Round(individualStopwatch.appeased.total毫秒));
StopWatch时间[i]=单个StopWatch.Appeased.TotalMillistics;
个人秒表。重置();
}
globalStopwatch.Stop();
WriteLine(“总时间:{0}s”,Math.Round(globalStopwatch.appeased.TotalSeconds,2));
WriteLine(“Average:{0}ms”,Math.Round(stopwatchTimes.Average(time=>time));
返回秒表时间;
},TaskCreationOptions.LongRunning);
}
测试结果:

.NET核心

  • 平均:761ms
  • 总时间:38s
.NET框架

  • 平均:638ms
  • 总时间:32秒
.NET Core不仅在与CPU相关的任务上速度较慢。它在磁盘I/O任务上也比较慢


你知道为什么.NETCore在这方面会慢一点吗?是否可以进行更改以提高.NET Core的性能?

.NET Framework项目默认为32位代码。此选项在项目的生成设置中可见,默认情况下处于选中状态。NET核心项目默认为64位代码。如果您取消选中“首选32位”框,您将注意到.NET Framework的性能下降


另一点需要注意的是,桌面x86 JIT是一个单独的代码 x64 JIT的基础。对于64位,.NET Framework和.NET Core 现在就使用龙井;对于32位.NET内核,仍然使用RyuJIT,但是.NET 框架使用传统的JIT,因此您可以获得两种不同的比特率 和一个不同的抖动


答案在Hans Passant和Jeroen Mostert的评论中提供。

这应该在.Net Core 2.0.7和.Net Framework 4.7.2中通过


根本原因是JIT的公共子表达式消除(又名CSE)优化中的一个错误。有关血淋淋的详细信息,请参阅本期(从PR链接而来)。

@series0ne我想是的。根据.NET的说法,内核似乎有一个额外的层,称为运行时适应层。这可能会导致开销。请尝试获得更准确的结果。确保在版本配置中运行测试。比较苹果和橙子是一个容易犯的错误。.NETFramework项目默认运行32位代码,.NETCore喜欢64位代码。非常不同的浮点重载代码。取消选中“首选32位”复选框,然后重试。为心理调试另一个复选框打分。请随意使用猜测来完成此Q+A。另一个值得注意的是,桌面x86 JIT是与x64 JIT分开的代码库。对于64位,.NETFramework和.NETCore现在都使用RyuJIT;对于32位的.NET内核,仍然使用RyuJIT,但是.NET Framework使用传统的JIT,因此您得到了不同的位和不同的抖动。