【问题标题】:How can I increase the speed of filtering a list如何提高过滤列表的速度
【发布时间】:2022-01-13 23:32:35
【问题描述】:

我在数据网格中显示数据,并希望使用范围滑块(带两个手柄的滑块)过滤数据。范围滑块的 change-event 只设置字符串变量filterTrigger。过滤器本身是通过 mouseup 事件触发的。

private void ApplyFilter()
{
    if (filterTrigger != "")
    {
        filteredData.Clear();

        suitableData.ForEach((item) =>
        {
            filteredData.Add(item); // create not referenced copy of list suitableData that was created in time consuming calculations
        });

        switch (filterTrigger)
        {
            case "foo":
                // remove too small and too large Foos
                _ = filteredData.RemoveAll(x => x.Foo > fooRangeSliderHandlesMinMax.ElementAt(1) || x.Foo < fooRangeSliderHandlesMinMax.ElementAt(0));
                // set new minimum and maximum of range of range slider
                barRangeSliderMinimum = filteredData.Min(x => x.Bar) - 0.1;
                barRangeSliderMaximum = filteredData.Max(x => x.Bar) + 0.1;
                // set new position of range slider handles
                barRangeSliderHandlesMinMax = new double[2] { Math.Max(barRangeSliderHandlesMinMax.ElementAt(0), barRangeSliderMinimum + 0.1), Math.Min(barRangeSliderHandlesMinMax.ElementAt(1), barRangeSliderMaximum - 0.1) };
                break;
            case "bar":
                _ = filteredData.RemoveAll(x => x.Bar > barRangeSliderHandlesMinMax.ElementAt(1) || x.Bar < barRangeSliderHandlesMinMax.ElementAt(0));
                fooRangeSliderMinimum = filteredData.Min(x => x.Foo) - 0.1;
                fooRangeSliderMaximum = filteredData.Max(x => x.Foo) + 0.1;
                fooRangeSliderHandlesMinMax = new double[2] { Math.Max(fooRangeSliderHandlesMinMax.ElementAt(0), fooRangeSliderMinimum + 0.1), Math.Min(fooRangeSliderHandlesMinMax.ElementAt(1), fooRangeSliderMaximum - 0.1) };
                break;
            default:
                break;
            }

        // remove values of foo if filterTrigger was "bar" and vice versa
        _ = filteredData.RemoveAll(x => x.Foo > fooRangeSliderHandlesMinMax.ElementAt(1) || x.Foo < fooRangeSliderHandlesMinMax.ElementAt(0) || x.Bar > barRangeSliderHandlesMinMax.ElementAt(1) || x.Bar < barRangeSliderHandlesMinMax.ElementAt(0));

        // update data grid data
        IFilteredData = filteredData;

        dataGrid.Reload();

        filterTrigger = "";
    }
}

当我注释掉所有以丢弃 _ 开头的行时,代码运行流畅。但当然,我需要这些线条。问题是,它们需要大量的处理器能力。它仍在工作,但是当我点击过滤器的手柄移动鼠标时,手柄非常滞后(我的笔记本电脑听起来像直升机)。

我知道最后一个过滤器的一部分是多余的,因为当 filterTrigger 为 foo 时, foo 已经被过滤了。但是只过滤之前没有过滤的,并不能单独解决问题,因为上面我只展示了两个过滤器,但实际上大约有十个过滤器。

那么,有没有办法优化这段代码?

【问题讨论】:

  • 为什么要先将所有数据添加到filteredData,然后再进行修剪?只承认你确实想要的项目会更有意义
  • 你在filteredData有多少项目?
  • @MathiasR.Jessen:问得好。那不是很聪明。我添加了一个 if 条件来检查是否将 item 添加到 filteredData 并删除了整个 RemoveAll 部分。感谢您的出色提示。
  • @Chetan:suitableData 有 400 个项目,filteredData 有 0 到 400 个项目。这并不多,但每个项目都有大约 30 个属性,例如 item.Fooitem.Bar

标签: c# optimization


【解决方案1】:

优化代码时,第一条规则是衡量,最好使用能准确告诉您代码的哪一部分花费大部分时间的分析器。

第二个规则是使用最佳算法,但除非您有大量项目和一些合理的方式来对所述项目进行排序或索引,否则线性时间是您能做的最好的。

以下是一些可能需要改进的猜测和建议:

  • 避免使用.ElementAt,这可能会创建一个新的枚举器对象,这需要一些时间。尤其是在内部循环中。更喜欢使用索引器和/或将其存储在局部变量中。
  • 避免使用 Linq。 Linq 的可读性很好,但它会产生一些开销。因此,在优化时,可能值得使用常规循环来查看开销是否很大。
  • 尝试一次性完成所有处理。而是遍历所有项目一次以找到最小值,一次遍历最大值,同时进行这两项操作。内存速度很慢,在已缓存的项目中尽可能多地处理它有助于减少内存流量。
  • 我会考虑将RemoveAll 替换为一个循环,该循环将通过检查的项目复制到一个空列表中。这应该有助于确保项目最多被复制一次。

优化时的经验法则是使用低级语言功能。这些通常更容易使抖动得到很好的优化。但这可能会使代码更难阅读,因此请使用分析器来优化最需要它的地方。

【讨论】:

  • 谢谢,很有帮助。我实施了你的提示。该应用程序现在运行流畅。我还添加了一些Stopwatches。整个方法ApplyFilter()在0ms内执行。所以,我认为,主要问题不在于执行速度本身,因为 ApplyFilter() 不是由 Change 事件触发的,而是在鼠标抬起时触发的,所以不经常触发。但也许在后台,我的所有列表和这些列表的副本都需要大量内存。我优化的最后一件事是,我没有通过 Change 事件设置 filterTrigger,而是在鼠标按下时设置。因此,它只设置一次,而不是在句柄移动的每个像素上设置一次。
  • @Flippowitsch 这就是您首先测量的原因。大部分时间花在一些完全出乎意料的部分上并不少见。
猜你喜欢
  • 1970-01-01
  • 2012-01-18
  • 1970-01-01
  • 2010-10-21
  • 1970-01-01
  • 2011-10-16
  • 2019-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多