首次呼叫扩展方法比后续呼叫慢

时间:2010-08-11 15:37:16

标签: c# wpf performance extension-methods

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

例如,对于一小组数据,进行计算似乎需要大约5秒钟,而每次后续调用都是一秒左右。

谢谢, WTS

代码看起来像这样:

测试代码

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();
}

扩展方法(ObservableItems全部在不同的线程上创建)

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 个答案:

答案 0 :(得分:4)

大多数情况下,第一次调用方法时,会有一些与JIT编译时间相关的开销。这会产生影响(尽管很可能没那么多)。

但是,查看代码时,您需要花费大量时间等待通过调度程序对UI进行编组的异步调用。这将对您的整体表现造成很大影响,并且放慢速度。

我建议在一个调度调用中执行所有操作,并使用Invoke而不是BeginInvoke。而不是每个项目编组一条消息,而只是封送包含foreach循环项目的单个代理。

这将明显加快。

答案 1 :(得分:0)

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

该属性的格式为:

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

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