你能清楚C#中的缓存和优化吗?

时间:2011-05-02 06:07:58

标签: c# .net caching optimization

我正在尝试测试内置集合在某些应用程序(例如交叉点)下的最佳性能。为此,我构建了以下测试:

private static void Main(string[] args)
{
    LoadTest<HashSet<object>>();
    ClearEverythingHere(); // <<-- what can go here?
    LoadTest<LinkedList<object>>();
    Console.ReadKey(true);
}

private static void LoadTest<T>() where T : ICollection<object>, new()
{
    const int n = 1 << 16;
    const int c = 1 << 3;

    var objs = new object[n << 1];
    for (int i = 0; i < n << 1; i++)
        objs[i] = new object();

    var array = new T[c];
    var r = new Random(123);
    for (int s = 0; s < c; s++)
    {
        array[s] = new T();
        for (int i = 0; i < n; i++)
            array[s].Add(objs[r.Next(n << 1)]);
    }

    var sw = Stopwatch.StartNew();
    IEnumerable<object> final = array[0];
    for (int s = 1; s < c; s++)
        final = final.Intersect(array[s]);
    sw.Stop();
    Console.WriteLine("Ticks elapsed: {0}", sw.ElapsedTicks);
}

如果我从Main取消注释两种测试方法,那么无论我测试结构的顺序如何,第二次测试总是比第一次完成得快得多。通常,第一个交叉点以几百个刻度运行,第二个交叉点以不到十个为单位完成。我原以为在完全独立的范围内进行测试会阻止至少一些(我假设的)缓存导致这种不同的结果。

是否有一种简单的方法可以重置应用程序,以便我不必担心缓存或优化测试?我希望能够运行一个测试,打印结果,清除它,并运行另一个测试?是的,我可以评论和取消注释,或者可能产生两个单独的应用程序,但这对于简单的控制台测试来说是很多工作。


修改:我已根据答案中的建议修改了测试。

private static void Main(string[] args)
{
    const int n = 1 << 17;
    const int c = 1 << 4;

    var objs = new Item[n << 1];
    for (int i = 0; i < (n << 1); i++)
        objs[i] = new Item(i);

    var items = new Item[c][];
    var hash = new HashSet<Item>[c];
    var list = new LinkedList<Item>[c];

    var r = new Random();
    for (int s = 0; s < c; s++)
    {
        items[s] = new Item[n];
        for (int i = 0; i < n; i++)
            items[s][i] = objs[r.Next(n << 1)];
        hash[s] = new HashSet<Item>(items[s]);
        list[s] = new LinkedList<Item>(items[s]);
    }

    Stopwatch stopwatch = Stopwatch.StartNew();
    HashSet<Item> fHash = hash[0];
    for (int s = 1; s < hash.Length; s++)
        fHash.IntersectWith(hash[s]);
    stopwatch.Stop();

    Console.WriteLine("Intersecting values: {0}", fHash.Count);
    Console.WriteLine("Ticks elapsed: {0}", stopwatch.ElapsedTicks);

    stopwatch = Stopwatch.StartNew();
    IEnumerable<Item> iEnum = list[0];
    for (int s = 1; s < list.Length; s++)
        iEnum = iEnum.Intersect(list[s]);
    Item[] array = iEnum.ToArray();
    stopwatch.Stop();

    Console.WriteLine("Intersecting values: {0}", array.Length);
    Console.WriteLine("Ticks elapsed: {0}", stopwatch.ElapsedTicks);
    Console.ReadKey(true);
}

[DebuggerDisplay("Value = {_value}")]
private class Item
{
    private readonly int _value;

    public Item(int value)
    {
        _value = value;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (obj.GetType() != typeof(Item))
            return false;
        return Equals(obj);
    }

    public override int GetHashCode()
    {
        return _value;
    }

    public override string ToString()
    {
        return _value.ToString();
    }
}

这解决了我的大部分问题。 (如果您想知道,HashSet.IntersectWith的显示速度比IEnumerable.Intersect快得多。)

1 个答案:

答案 0 :(得分:2)

您的代码中存在少量错误。

  1. Intersects是LINQ函数,因此它意味着它被懒惰地评估。这意味着只有在数据被加入时才会执行。这可以通过循环数据或在此枚举上调用ToList或ToArray来完成。通过添加此项,您将获得不同的结果
  2. 必须始终对相同数据进行测试。尝试在测试方法之外创建数据并将其作为参数传递。
  3. 第一遍代码通常被认为是错误的,因为JITing等。
  4. 尝试创建自己的对象并覆盖Equals和GetHashCode。像这样,测试它可能是不正确的。