.Net 4 中巨大的性能差异背后的原因是什么

2023-12-27

我只是在对红黑树进行一些研究。我知道.Net 4.0 中的 SortedSet 类使用 RedBlack 树。因此,我使用 Reflector 取出该部分并创建了一个 RedBlackTree 类。现在我正在这个 RedBlackTree 和 SortedSet 上运行一些性能测试,插入 40000 个连续整数值(从 0 到 39999),我惊讶地发现存在巨大的性能差异,如下所示:

 RBTree    took 9.27208   sec to insert 40000 values 
 SortedSet took 0.0253097 sec to insert 40000 values

其背后的原因是什么?顺便说一句,我仅在发布配置中运行了测试,这是小测试代码

            var stopWatch = new Stopwatch();
            var rbT = new RedBlackTree<int>();      
        stopWatch = new Stopwatch();
        stopWatch.Start();
        for (int i = 0; i < 40000; i++) {
            rbT.Add(i);
        }
        stopWatch.Stop();
        Console.WriteLine(stopWatch.Elapsed);

        var ss = new SortedSet<int>();
        stopWatch = new Stopwatch();
        stopWatch.Start();
        for (int i = 0; i < 40000; i++) {
            ss.Add(i);
        }
        stopWatch.Stop();
        Console.WriteLine(stopWatch.Elapsed);

Edit

我想分享我提取的 RBTree 代码,以便您也可以运行诊断

public class Node<T>
    {
        public Node(){}

        public Node(T value)
        {
            Item = value;
        }       

        public Node(T value, bool isRed)
        {
            Item = value;
            IsRed = isRed;
        }

        public T Item;
        public Node<T> Left;
        public Node<T> Right;
        public Node<T> Parent;
        public bool IsRed;
    }

    public class RedBlackTree<T>
    {
        public RedBlackTree(){} 

        public Node<T> root;
        int count, version; 
        Comparer<T> comparer = Comparer<T>.Default;     

        public void Add(T item)
        {
            if (this.root == null)
            {
                this.root = new Node<T>(item, false);
                this.count = 1;
                this.version++;
                return;
            }

            Node<T> root = this.root;
            Node<T> node = null;
            Node<T> grandParent = null;
            Node<T> greatGrandParent = null;
            this.version++;

            int num = 0;
            while (root != null)
            {
                num = this.comparer.Compare(item, root.Item);
                if (num == 0)
                {
                    this.root.IsRed = false;
                    return;
                }
                if (Is4Node(root))
                {
                    Split4Node(root);
                    if (IsRed(node))
                    {
                        this.InsertionBalance(root, ref node, grandParent, greatGrandParent);
                    }
                }
                greatGrandParent = grandParent;
                grandParent = node;
                node = root;
                root = (num < 0) ? root.Left : root.Right;
            }
            Node<T> current = new Node<T>(item);
            if (num > 0)
            {
                node.Right = current;
            }
            else
            {
                node.Left = current;
            }
            if (node.IsRed)
            {
                this.InsertionBalance(current, ref node, grandParent, greatGrandParent);
            }
            this.root.IsRed = false;
            this.count++;
        }


        private static bool IsRed(Node<T> node)
        {
            return ((node != null) && node.IsRed);
        }

        private static bool Is4Node(Node<T> node)
        {
            return (IsRed(node.Left) && IsRed(node.Right));
        }

        private static void Split4Node(Node<T> node)
        {
            node.IsRed = true;
            node.Left.IsRed = false;
            node.Right.IsRed = false;
        }

        private void InsertionBalance(Node<T> current, ref Node<T> parent, Node<T> grandParent, Node<T> greatGrandParent)
        {
            Node<T> node;
            bool flag = grandParent.Right == parent;
            bool flag2 = parent.Right == current;
            if (flag == flag2)
            {
                node = flag2 ? RotateLeft(grandParent) : RotateRight(grandParent);
            }
            else
            {
                node = flag2 ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent);
                parent = greatGrandParent;
            }
            grandParent.IsRed = true;
            node.IsRed = false;
            ReplaceChildOfNodeOrRoot(greatGrandParent, grandParent, node);
        }

        private static Node<T> RotateLeft(Node<T> node)
        {
            Node<T> right = node.Right;
            node.Right = right.Left;
            right.Left = node;
            return right;
        }

        private static Node<T> RotateRight(Node<T> node)
        {
            Node<T> left = node.Left;
            node.Left = left.Right;
            left.Right = node;
            return left;
        }

        private static Node<T> RotateLeftRight(Node<T> node)
        {
            Node<T> left = node.Left;
            Node<T> right = left.Right;
            node.Left = right.Right;
            right.Right = node;
            left.Right = right.Left;
            right.Left = left;
            return right;
        }

        private static Node<T> RotateRightLeft(Node<T> node)
        {
            Node<T> right = node.Right;
            Node<T> left = right.Left;
            node.Right = left.Left;
            left.Left = node;
            right.Left = left.Right;
            left.Right = right;
            return left;
        }

        private void ReplaceChildOfNodeOrRoot(Node<T> parent, Node<T> child, Node<T> newChild)
        {
            if (parent != null)
            {
                if (parent.Left == child)
                {
                    parent.Left = newChild;
                }
                else
                {
                    parent.Right = newChild;
                }
            }
            else
            {
                this.root = newChild;
            }
        }
    }

Edit


我对其他一些数据结构(一些由我创建*,一些来自 .net 框架**)运行了相同的诊断,这是有趣的结果

*AATree                 00:00:00.0309294
*AVLTree                00:00:00.0129743
**SortedDictionary      00:00:00.0313571
*RBTree                 00:00:09.2414156
**SortedSet             00:00:00.0241973

RBTree 与上面相同(从 SortedSet 类中剥离)。我也尝试过 400000 个值,但 RBTree 似乎永远需要,我实在不知道为什么。


你的程序中有一个错误Node<T>班级。当您调用仅采用单个值参数的构造函数时,您应该设置IsRed to true.

我认为固定的Node<T>类应该看起来像这样:

public sealed class Node<T>
{
    public T Item { get; private set; }
    public bool IsRed { get; set; }
    public Node<T> Left { get; set; }
    public Node<T> Right { get; set; }

    public Node(T value)
    {
        Item = value;
        IsRed = true;
    }

    public Node(T value, bool isRed)
    {
        Item = value;
        IsRed = isRed;
    }
}

另一种选择——我的偏好——是完全省略该构造函数并始终需要IsRed在实例化新节点时显式设置:

public sealed class Node<T>
{
    public T Item { get; private set; }
    public bool IsRed { get; set; }
    public Node<T> Left { get; set; }
    public Node<T> Right { get; set; }

    public Node(T value, bool isRed)
    {
        Item = value;
        IsRed = isRed;
    }
}

然后替换你的这一行Add方法...

Node<T> current = new Node<T>(item);

...有了这个...

Node<T> current = new Node<T>(item, true);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.Net 4 中巨大的性能差异背后的原因是什么 的相关文章

  • 隐式方法组转换陷阱

    我想知道为什么给定代码的输出 在 LinqPad 中执行 void Main Compare1 Action Main Dump Compare2 Main Dump bool Compare1 Delegate x return x Ac
  • 泛型与接口的实际优势

    在这种情况下 使用泛型与接口的实际优势是什么 void MyMethod IFoo f void MyMethod
  • C++ 中的单例和抽象基类

    最近我遇到了关于实现 Singleton 但涉及抽象基类的问题 假设我们有这样的类层次结构 class IFoo it s ABC class Foo public IFoo 我们的单例类定义如下 template
  • 我们如何将数据从一个打开的表单传递到另一个打开的表单?

    winform中如何将数据从一个窗体传递到另一个打开的窗体 在 Windows 应用程序中 一个窗体打开另一个窗体 当我在父表单中输入一些数据时 这些数据将立即反映在另一个子表单中 这将如何发生 取决于你想要多花哨 最简单的方法就是直接调用
  • 在桌面应用程序中,类库的连接字符串存储在哪里?我可以在app.config中使用吗?

    我是桌面应用程序开发的新手 目前正在使用分层架构 用户界面 DAL BLL 构建桌面应用程序 在 Web 开发中 我曾经将连接字符串存储在 web config 中 我的类库从那里访问它 请指导我在桌面应用程序中如何以及在何处存储 DAL
  • 使用静态类型代替变量

    当您的项目不使用命名空间时 有什么方法可以告诉编译器使用静态类型而不是变量吗 例如 我有一个名为 User 的类 它具有各种静态和非静态方法 假设调用了其中一个静态方法GetUser 我想称之为User GetUser 方法来自一个方法 该
  • 将列表(对象)转换为列表(字符串)

    有没有办法转换List of Object to a List of String 在 c 或 vb net 中而不迭代所有项目 幕后迭代很好 我只想要简洁的代码 Update 最好的方法可能就是进行新的选择 myList Select f
  • 具有多重继承的类的 sizeof

    首先 我知道 sizeof 取决于机器和编译器的实现 我使用的是 Windows 8 1 x64 gcc 5 3 0 没有标志传递给编译器 我从大学讲座中得到了以下代码 include
  • 如何在 C++ 中对静态缓冲区执行字符串格式化?

    我正在处理一段对性能要求非常高的代码 我需要执行一些格式化的字符串操作 但我试图避免内存分配 甚至是内部库的内存分配 在过去 我会做类似以下的事情 假设是 C 11 constexpr int BUFFER SIZE 200 char bu
  • C 中“for”循环中的两个变量

    我正在编写一些代码 需要在其中使用两个变量for环形 下面的代码看起来没问题吗 它确实给了我预期的结果 for loop 1 offset loop 2 offset 2 loop 1 gt offset 190 loop 2 lt 190
  • 以标准用户身份打开默认浏览器 (C++)

    我目前正在使用 ShellExecute 打开 在用户浏览器中打开 URL 但在 Win7 和 Vista 中遇到了一些麻烦 因为该程序作为服务运行提升 当 ShellExecute 打开浏览器时 它似乎读取 本地管理员 配置文件而不是用户
  • 套接字:监听积压并接受

    listen sock backlog 在我看来 参数backlog限制连接数量 这是我的测试代码 server initialize the sockaddr of server server sin family AF INET ser
  • 如何使用 libpq 获取双精度值?

    The examples http www postgresql org docs 9 3 interactive libpq example htmllibpq 文档中展示了如何通过将整数值转换为主机字节序表示来获取整数值 我很好奇必须做
  • Qt:将拖放委托给子级的最佳方式

    我在 QWidget 上使用拖放 我重新实现了 DragEnterEvent dragLeaveEvent dragMoveEvent 和 dropEvent 效果很好 在我的 QWidget 中 我有其他 QWidget 子级 我希望它们
  • SignTool 错误:访问被拒绝

    我尝试在安装了 VS2010 的 Windows Server 2008 R2 x64 上使用新的代码签名证书对 NET 应用程序进行authenticode 签名 但 SignTool 始终响应访问被拒绝 SignTool exe sig
  • Windows:列出并启动与扩展关联的应用程序

    如何确定与特定扩展名 例如 JPG 关联的应用程序 然后确定该应用程序的可执行文件所在的位置 以便可以通过调用 System Diagnostics Process Start 来启动它 我已经知道如何读取和写入注册表 注册表的布局使得以标
  • 使用 roslyn 扩展 C# 语法

    我试图在没有 else 情况的情况下实现 return if return value if 因为我只想在条件有效时返回或返回一个值 我知道 有if condition return or if condition return value
  • 在类中使用 std::chrono::high_resolution_clock 播种 std::mt19937 的正确方法是什么?

    首先 大家好 这是我在这里提出的第一个问题 所以我希望我没有搞砸 在写这篇文章之前我用谷歌搜索了很多 我对编码 C 很陌生 我正在自学 考虑到有人告诉我 只为任何随机引擎播种一次是一个很好的做法 我在这里可能是错的 什么是正确 最佳 更有效
  • 具有多种类型的 C# 泛型类型推断

    我有以下通用方法 用于将一种类型的输入对象序列化为超类型 如下所示 public string SerialiseAs
  • FakeItEasy 代理方法调用实际实现

    我正在尝试将对假对象的调用代理到实际的实现 这样做的原因是我希望能够使用 Machine Specifications 的 WasToldTo 和 WhenToldTo 它们仅适用于接口类型的伪造 因此 我正在执行以下操作来代理对我的真实对

随机推荐

  • component.clientId 和 p:component() 生成的客户端 id 之间的区别

    我正在尝试检索 p dataList 内的 h panelGroup 的客户端 ID 我尝试了两种方法 1 使用component clientId 例如
  • 为 Laravel Blade 模板解析字符串而不是文件

    我需要缓存 CMS 生成的远程刀片模板 以使应用程序的公共接口保持最新 理想情况下 我可以使用 file get contents 和缓存每周检查一次更新 有没有办法让 Laravel 使用变量的内容而不是文件作为刀片模板 我找不到让 La
  • Pylance 不允许我导航到源代码,而是将我带到 .pyi 存根

    我正在使用 pylance 作为 vs code 它工作得非常好 除了因为我使用它 当我尝试从已安装的库检查代码时 我只能得到存根 我认为是由 pylance 生成的 For example the information shown ab
  • 有没有办法获取unix套接字连接另一端的uid

    有没有办法让 UNIX 域套接字侦听器仅接受来自特定用户的连接 chmod chown不适用于抽象套接字 afaik 或者换句话说 获取传入连接的 uid 在 Linux 上 Dbus 在Linux上使用抽象unix socket 有一个功
  • 如何在数组中搜索子字符串匹配项?

    我需要在 JavaScript 中搜索数组 搜索将仅匹配字符串的一部分 因为字符串将包含其他组件 然后我需要返回成功匹配的数组元素和完整字符串 Example const windowArray item thing id 3 text c
  • Android Seekbar - 只允许用拇指进行更改?

    我有一个搜索栏 我只想允许用拇指 手柄部分进行更改 如果用户点击其他任何地方 我希望忽略该点击 是否有固有的属性可以做到这一点 如果没有 我已经知道我可以设置一个 onTouch 监听器并返回true 禁用 它 但是有没有办法检测拇指何时被
  • 打字稿和 d3

    我有一个使用 d3 库的应用程序 在打字稿代码中 为了成功使用 d3 即 没有编译器错误 TS2686 d3 引用 UMD 全局 但当前文件是模块 我必须包含以下行 import as d3 from d3 问题是它会发出 require
  • QT,如何聚焦虚拟键盘,并使用键盘控制虚拟键盘

    我在嵌入式设备上使用Qt5的虚拟键盘 没有鼠标 键盘不是完整的PC键盘 而是只有十一个键 包括上 下 左 右 enter esc 所以我想专注于虚拟键盘 并使用上 下 左 右 回车来控制虚拟键盘 模拟鼠标点击虚拟键 怎么做 doc qt i
  • 如何使用 winmerge 行过滤器忽略包含特定单词的行?

    我有许多文件包含以下类型的行 version Revision 1 xxx 我希望在使用 winmerge 进行比较时忽略这种类型的行 我尝试过使用线路滤波器 但是 直到无法做到这一点 有人可以在这方面帮助我吗 你实际上可以这样做线路滤波器
  • 如何获取 Facebook 用户的信息并将其插入数据库?

    我不知道如何问它 但我正在尝试自学如何创建一个使用图形 api 的程序 我见过的大多数教程都是较旧的 我不知道它们现在有多相关 本质上 我试图获取有人点击我的应用程序的 东西 它说 这个应用程序需要您的用户名等 然后允许或不允许 我希望它获
  • 在组合键中使用 JPA AttributeConverter 时,Spring-data/hibernate 查询中不支持 JPA AttributeConverter

    这是我的枚举 public enum FooBarType Foo Bar javax persistence Converter public static class Converter implements AttributeConv
  • java / Android 一个进度条下载多个文件

    我在 for 循环的帮助下在 AsyncTask 中下载多个文件 下面的代码工作正常 但下载的每个文件都有自己的单个进度条 我只想为所有下载的文件提供一个进度条 ProgressDialog for downloading images O
  • 在 PowerBI 中使用 ALLEXCEPT 实现分组百分比的问题

    我想获得一个分组百分比作为新列或新度量 这是我在论坛中读到的内容推荐的 我有一个数据 用户将使用切片器来获取各种百分比 然而 目前我的查询一直失败 我访问过论坛 但似乎不知道如何分组并获取组的百分比 此外 我无法使用查询编辑器中的分组依据工
  • 以 JSON 格式将数据从 Kafka Topic 推送到 PostgreSQL

    更新后出错 2019 07 29 12 52 23 301 INFO Initializing writer using SQL dialect PostgreSqlDatabaseDialect io confluent connect
  • Joomla 如何自定义主菜单

    我正在学习 joomla 并面临下一个问题 这是 HTML 格式的主菜单 ul li class active a href home a li li a href bio a li li a href news a li li a hre
  • 反向特征查找?

    我一直在寻找解决以下问题的设计 需要几句话来描述 我们有四种类型 A1 A2 B 和 C 我们想编写一个函数 fn 它以类型 P 作为参数 使用特征 P 在 A1 A2 B C 内解析为 PA1 PA2 PB PC fn 的实现对于 PA1
  • 如何计算硬币问题的可能组合

    我正在尝试实现一个硬币问题 问题规范是这样的 创建一个函数来计算可用于给定金额的所有可能的硬币组合 All possible combinations for given amount 15 coin types 1 6 7 1 1 1 1
  • TEdgeBrowser 弹出菜单/上下文菜单

    如何覆盖 TEdgebrowser 组件中的上下文菜单 财产检查员不为此提供活动 可以使用 Javascript 停用上下文菜单 该解决方案并未涵盖所有用例 对于我的问题来说已经足够了
  • ActiveSupport::Deprecation.silenced = true 对我不起作用?

    我的应用程序是使用 Ruby 1 8 7 和 Rails 2 3 11 开发的 我在运行 rake spec 时收到很多弃用警告 DEPRECATION WARNING ActiveSupport Dependencies load pat
  • .Net 4 中巨大的性能差异背后的原因是什么

    我只是在对红黑树进行一些研究 我知道 Net 4 0 中的 SortedSet 类使用 RedBlack 树 因此 我使用 Reflector 取出该部分并创建了一个 RedBlackTree 类 现在我正在这个 RedBlackTree