【问题标题】:NDepend searching for faster collection opportunitiesNDepend 寻找更快的收集机会
【发布时间】:2016-07-22 17:33:30
【问题描述】:

我有一个庞大的遗留代码库,我想对其进行优化,使其更快。 出于这个原因,我考虑寻找可以用 HashSet 和字典替换列表和数组的机会。

.NET Framework Usage/System.collection下有如下NDepend查询

// <Name>Caution with List.Contains()</Name>
let containsMethods = ThirdParty.Methods.WithFullNameIn(
   "System.Collections.Generic.List<T>.Contains(T)",
   "System.Collections.Generic.IList<T>.Contains(T)",
   "System.Collections.ArrayList.Contains(Object)")

from m in Application.Methods.UsingAny(containsMethods) 
select m

这个查询是不够的。它将列出一个函数,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ListOptimisation
{
    class Program
    {
        static void Main(string[] args)
        {
            int aLength = 10000;
            List<int> aNumbers2Search = Enumerable.Range(0, aLength).ToList();

            List<int> aTestList = Enumerable.Range(0, aLength).ToList();
            int[] aTestArray = Enumerable.Range(0, aLength).ToArray();

            HashSet<int> aTestHash = new HashSet<int>(Enumerable.Range(0, aLength));
            Dictionary<int, int> aTestDictionary = new Dictionary<int, int>();
            for(int i = 0; i < aLength; ++i)
            {
                aTestDictionary.Add(i, i);
            }

            Search(aTestList, aNumbers2Search);
            SearchIList(aTestList, aNumbers2Search);
            SearchIEnumerable(aTestList, aNumbers2Search);
            Search(aTestArray, aNumbers2Search);
            SearchIList(aTestArray, aNumbers2Search);
            SearchIEnumerable(aTestArray, aNumbers2Search);
            Search(aTestHash, aNumbers2Search);
            SearchIEnumerable(aTestHash, aNumbers2Search);
            Search(aTestDictionary, aNumbers2Search);
        }

        private static void Search(List<int> testList_in, List<int> numbers2Search_in)
        {
            numbers2Search_in.ForEach(x => testList_in.Contains(x));
        }

        private static void Search(HashSet<int> testHash_in, List<int> numbers2Search_in)
        {
            numbers2Search_in.ForEach(x => testHash_in.Contains(x));
        }

        private static void Search(Dictionary<int, int> testDictionary_in, List<int> numbers2Search_in)
        {
            numbers2Search_in.ForEach(x => testDictionary_in.ContainsKey(x));
        }

        private static void Search(int[] testArray_in, List<int> numbers2Search_in)
        {
            numbers2Search_in.ForEach(x => testArray_in.Contains(x));
        }

        private static void SearchIList(IList<int> testIList_in, List<int> numbers2Search_in)
        {
            numbers2Search_in.ForEach(x => testIList_in.Contains(x));
        }

        private static void SearchIEnumerable(IEnumerable<int> testIEnumerable_in, List<int> numbers2Search_in)
        {
            numbers2Search_in.ForEach(x => testIEnumerable_in.Contains(x));
        }
    }
}

更好的查询是这个:

// <Name>Caution with List style contains</Name>
let containsMethods = ThirdParty.Methods.WithSimpleName("Contains").Except(ThirdParty.Methods.WithFullNameIn("System.Collections.Generic.HashSet<T>.Contains(T)"))

from m in Application.Methods.UsingAny(containsMethods) 
select m

//<Description>
// Alternative to Caution with List.Contains()
//</Description>

这将列出 4 个函数(List、IList、int[]、IEnumerable)。 我是关于 CQLinq 的新手。 我的问题是:

  • 是否有人可以编写更好的查询来检测可能的不良 .NET 容器使用情况(不仅针对包含,还针对其他可能的操作)?
  • 您如何或将如何检测容器使用不当?

最后一点,我们的一些业务逻辑处理大量数据,因此拥有正确的容器、数据结构和算法很重要。

【问题讨论】:

    标签: c# .net performance ndepend cqlinq


    【解决方案1】:

    确实,尝试用Hashset&lt;T&gt;.Contains() 调用替换List&lt;T&gt;.Contains() 调用不是微优化,并且可以显着提高性能。实际上,根据我的经验,重构算法以依赖 O(1) 哈希集搜索是提高性能的最佳方法之一。

    您编写的 CQLinq 查询是识别一些潜在慢点的第一步。但是,要开始良好地重构,您必须 1) 在运行时查看代码以评估集合大小,以及 2) 在实际情况下使用performance profiling tool 来评估这些潜在的慢点是否会产生影响在性能方面,还可以找到与查询不匹配的其他慢点。

    【讨论】:

    • 我们有业务场景性能测试,所以我可以验证我的更改,我可以将更改的代码与相应的测试相匹配,所以这不是问题。 :-) 我的终极梦想是有人会通过收集称为“可能的性能瓶颈”的“完善的”CQLinq 查询来回答我的第二个问题。 :-)
    • 您写的查询是正确的,但并不完美。如果在方法中创建了 Hashset 并且 ICollection.Contains() 是从 ICollection 对象上的另一个方法调用的,则 NDepend 不会聪明到猜测对 Contains() 的调用是的,它是静态分析器,而不是动态分析器;-)
    • 是的,我的查询会给出误报,但它会包含更多“气味”。是的,NDepends 不是动态分析器,也许我应该在 .NET List.Contains 上放置一个跟踪器断点...
    • 为什么不使用动态分析器?
    【解决方案2】:

    这不是优化性能问题的好方法。除非您处理大量列表,否则此优化将对您的系统产生轻微影响。

    使用performance profiling software 将获得更好的结果。如果您想通过搜索一些代码模式来提高性能,请尝试搜索嵌套循环和昂贵的代码,例如文件和数据库相关的方法。

    【讨论】:

    • 这不是微优化,我们能够在重写几个 List 处理时获得巨大的性能提升,O(n) ".Contains" 时间到 HashSet 和 Dictionary O(1 ) “.Contains”时间。
    • 您如何衡量性能改进?我知道我没有回答您的问题,但您陈述了您的问题(性能)并描述了解决此问题的一种不常见的方法(并且您的查询已经捕获了所有相关的包含 IMO),也许您应该编辑您的问题以提供有关系统的更多信息以及您是如何执行此绩效改进流程的。
    • 我们进行了性能测试,可以进行业务可理解的场景,并记录其运行时间。是的,我们发现了一些瓶颈,分析起来非常冗长乏味,最后是容器使用不当。我们无法分析所有内容,代码库很大(数百万行),对我来说是遗产。
    猜你喜欢
    • 2011-09-24
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2010-12-17
    • 2010-11-24
    • 1970-01-01
    • 2012-06-10
    相关资源
    最近更新 更多