【问题标题】:Huge Performance Difference with HashSet between .NET Framework 4.8 and .NET 5.NET Framework 4.8 和 .NET 5 与 HashSet 的巨大性能差异
【发布时间】:2021-04-02 03:00:48
【问题描述】:

我的问题是应用程序非常慢,因为有数千个处理程序附加到它的事件并且删除这些处理程序真的很慢。我的应用程序作为 .NET Framework 4.8 进程运行,

我编写了一个测试程序(但使用 .NET 5),我用自己的实现替换了事件,我使用 HashSet 来存储处理程序并自己实现事件的添加/删除处理程序。

我的测试应用程序运行良好,但将此解决方案应用于我的实际应用程序后,性能仍然很差。

我意识到运行时差异似乎是原因。以下示例程序运行速度很快(在 .NET 5 上大约需要 1 秒,但如果在 .NET Framework 4.8 应用程序中运行相同的代码,则运行几分钟):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;

namespace EventPerformaneMeasurement
{
    class Program
    {
        private static void Main()
        {
            var sw = new Stopwatch();
            var mc = new MyClass();
            var list = Enumerable.Range(1, 1_000_000).Select(x => new PropertyChangedEventHandler((_, _) => { Console.WriteLine($"Handler {x} called"); })).ToList();
            
            sw.Restart();
            list.ForEach(handler=>mc.PropertyChangedNew += handler); 
           
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            
            sw.Restart();
            list.ForEach(handler=>mc.PropertyChangedNew -= handler); 
            
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }

    class MyClass
    {
        private HashSet<PropertyChangedEventHandler> _handlers = new();
        
        public event PropertyChangedEventHandler PropertyChangedNew
        {
            add => _handlers.Add(value);
            remove => _handlers.Remove(value);
        }
    }
}

我创建了两个 .NET fiddle 来重现此行为,.NET fiddle 要求我稍微更改示例代码的语法并将迭代次数从 1000000 减少到 15000,但性能差异仍然可见:

我不敢相信 .NET 5 和 .NET Framework 4.x 在 HashSet 性能方面存在如此大的差异。

有没有人知道更多关于这种效果或我可能被卡住的地方?有没有办法通过另一种方式在 .NET 4.8 中获得相同的性能?

【问题讨论】:

  • 您应该从 .NET Core/.NET 5 在多个方面比 .NET Framework 更快和更轻量级这一事实开始,因为大部分内容都是从头开始重写的。不确定这里是否可以做太多事情
  • 您是否在没有附加调试器的情况下运行它? (Visual Studio 中的 Ctrl+F5)
  • 我在没有调试器的情况下进行了测试,所以没有附加调试器。如果你看看这两个小提琴,你会看到大约。因子 4000,所以 2 毫秒对 8 秒!我很惊讶 .NET Framework 中的这个用例竟然这么慢……
  • HashTable 严重依赖于良好的 GetHashCode() 实现。比较 .NETCore.NETFramework。请注意错误修复和不太理想的解决方法,GetType().GetHashCode() 很糟糕,因为它为每个委托对象提供了相同的哈希码。这可能在他们需要修复的列表中,更改 .NETFramework 的实现风险太大。
  • 你好汉斯,谢谢。你的答案是正确的。它不能解决我的问题,但它回答了我的问题,如果 .NET Framework 中确实存在这样的性能问题。如果您将您的评论作为答案,我会将其标记为正确答案。

标签: .net performance hashset .net-5 .net-4.8


【解决方案1】:

在这种情况下,由于我不能等到我们可以将我们的产品切换到 .NET 5,我需要重构不使用事件/委托,而是使用带有接口的 Oberserver 模式并添加/删除侦听器方法。这样的观察者对象不会有这么弱的 GetHashCode 实现,所以 HashSet 会和这样的观察者对象一起执行……

【讨论】:

    猜你喜欢
    • 2021-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-26
    • 1970-01-01
    • 2020-10-16
    • 2011-04-12
    相关资源
    最近更新 更多