代码之家  ›  专栏  ›  技术社区  ›  Wonko the Sane

第一次调用扩展方法比后续调用慢

  •  0
  • Wonko the Sane  · 技术社区  · 14 年前

    我有一个通过一些扩展方法修改数据的类。为了调试性能,我创建了一些粗略的调试代码,多次使用相同的数据调用相同的方法。我发现,第一次通过循环进行计算总是比后续调用花费更长的时间。

    例如,对于一小部分数据,计算似乎需要5秒钟,而随后的每次调用大约需要1秒钟。

    水处理系统

    void TestCode()
    {
        for (int i = 0; i < iterationsPerLoop; i++)
        {
            DateTime startTime = DateTime.Now;
    
            // The test is actually being done in a BackgroundWorker
            dispatcher.Invoke(DispatcherPriority.Normal,
                                (Action)(() => this.PropertyCausingCodeToRun = "Run";
            while (this.WaitForSomeCondition)
                Thread.Sleep(125);
            DateTime endTime = DateTime.Now;
    
            double result = endTime.Subtract(startTime).TotalSeconds;
        }
    }
    

    private static List<ObservableItem> GetAvailableItems(MyObject myObject)
    {
        var items = new List<ObservableItem>(myObject.Items.ToList());
        var selectedItems = items.OrderByDescending(item => item.ASortableProperty)
                                 .SetItemIsAvailable(false)
                                 .SetItemPriority() 
                                 .OrderByDescending(item => item.Priority) 
                                 .Where(item => item.Priority > 0) 
                                 .SetItemIsAvailable(true) 
                                 .OrderBy(item => item.Date);
    
        return selectedItems.ToList();
    }
    

    扩展方法(observeItems都在不同的线程上创建)

    static class MyExtensionMethods
    {
        public static IEnumerable<T> SetItemIsAvailable<T>(this IEnumerable<T> sourceList,
                Boolean isAvailable) where T : ObservableItem
        {
            Action<T> setAvailable = i => i.IsAvailable = isAvailable;
    
            List<DispatcherOperation> invokeResults = new List<DispatcherOperation>();
    
            foreach (var item in sourceList)
            {
                invokeResults.Add(
                    item.ItemDispatcher.BeginInvoke(setAvailable , new object[] { item }));
            }
    
            invokeResults.ForEach(ir => ir.Wait());
            return sourceList;
        }
    
        public static IEnumerable<T> SetItemPriority<T>(this IEnumerable<T> sourceList) where T : ObservableItem
        {
            Action<T, double> setPriority = new Action<T, double>((item, priority) =>
                {
                    item.Priority = priority;
                });
    
            List<DispatcherOperation> invokeResults = new List<DispatcherOperation>();
    
            foreach (var item in sourceList)
            {
                double priority = ......;  // Some set of calculations
    
                invokeResults.Add(
                    item.ItemDispatcher.BeginInvoke(setPriority, 
                                new object[] { asset, priority }));
            }
    
            invokeResults.ForEach(ir => ir.Wait());
            return sourceList;
        }
    }
    
    2 回复  |  直到 14 年前
        1
  •  4
  •   Reed Copsey    14 年前

    但是,查看代码,您需要花费大量时间等待通过调度程序编组到UI的异步调用。这将给你的整体表现带来很大的冲击,并以这种方式降低速度。

    我建议您在一个调度调用中执行所有操作,并使用Invoke而不是BeginInvoke。不要为每个项封送一条消息,只需封送一个委托,该委托包含通过项的foreach循环。

    这将大大加快。

        2
  •  0
  •   Wonko the Sane    14 年前

    如我所知,真正的问题是由于最初调用属性对项进行排序(甚至在调用扩展方法之前)而引起的。

    属性的形式为:

    public Double ASortableProperty
    {
        get
        {
            if (mASortableProperty.HasValue)
            {
                return mASortableProperty.Value;
            }
            mASortableProperty = this.TryGetDoubleValue(...);
            return (mASortableProperty.HasValue ? mASortableProperty.Value : 0);
        }
    }
    

    因此,第一次通过循环时,这些值并没有从数据库中初始化,成本是在进行排序之前检索这些值。