清单收集,执行第一个动作

时间:2018-09-25 17:33:06

标签: c# .net list performance

我正在研究馆藏的表现,我注意到一个奇怪的List行为。首次访问列表的速度比后续访问速度慢得多,这可能与什么有关?

    static void Main(string[] args)
    {
        int k = 1000 * 1000;

        List<int> l = new List<int>();
        for (int i = 0; i < k; i++)
        {
            l.Add(i);
        }

        for (int i = 0; i < 10; i++)
        {
            var res = With_timer(() => l.IndexOf(0));
        }

        Console.ReadKey(true);
}

In this case, the first execute slower

UPD

private static T With_timer<T>(Func<T> action)
    {
        Stopwatch sw = Stopwatch.StartNew();
        var result = action();
        sw.Stop();

        Console.WriteLine($"TotalMilliseconds: {sw.Elapsed.TotalMilliseconds}");

        return result;
    }

UPD2 将动作移至其他方法

 private static void TestMethod(List<int> l)
    {
        for (int i = 0; i < 15; i++)
        {
            var res = With_timer(() => l.IndexOf(i));
        }
    }

然后在具有不同索引的main方法中两次调用它: enter image description here

2 个答案:

答案 0 :(得分:1)

速度是由于IndexOf方法和秒表jitted的首次访问所致。您可以修改代码,以使在循环外对这些方法的调用至少执行一次,一旦这样做,记录的访问时间都将保持一致。

完整MCVE

static void Main(string[] args)
{
    var unrelatedList = new List<int>(1) { 1 };
    var jitMe = unrelatedList.IndexOf(1);
    var sw = Stopwatch.StartNew();
    sw.Stop();
    Console.WriteLine($"Initialized {jitMe}, {sw.Elapsed.TotalMilliseconds}");
    Console.WriteLine();


    const int k = 1000 * 1000;
    var l = new List<int>(k);
    for (var i = 0; i < k; i++)
    {
        l.Add(i);
    }

    for (var i = 0; i < 10; i++)
    {
        sw = Stopwatch.StartNew();
        var itm = l.IndexOf(0);
        sw.Stop();
        Console.WriteLine($"TotalMilliseconds: {sw.Elapsed.TotalMilliseconds}, {itm}");
    }

    Console.WriteLine("Done");
    Console.ReadLine();
}

输出

Initialized 0, 0.0015

TotalMilliseconds: 0.0005, 0
TotalMilliseconds: 0.0005, 0
TotalMilliseconds: 0.0005, 0
TotalMilliseconds: 0, 0
TotalMilliseconds: 0, 0
TotalMilliseconds: 0.0005, 0
TotalMilliseconds: 0.0005, 0
TotalMilliseconds: 0.0005, 0
TotalMilliseconds: 0.0005, 0
TotalMilliseconds: 0.0005, 0

另请参阅C# JIT compiling and .NETCompiling MSIL to Native Code

答案 1 :(得分:0)

可能是正在为后备存储分配新的(初始)数组。 IIRC(已经有一段时间了),列表的后备存储的默认初始大小为10个元素。尝试循环11次而不是10次,看看下一次访问是否需要更多时间,因为它需要分配一个新数组并在幕后复制元素。
没关系,重新阅读代码,您正在检查访问权限,而不是插入。

您可能还会看到JIT流程的工件。如果将其移至一个单独的方法并多次调用该方法,两次调用中是否会看到类似的结果?

最后,查看doc.getElementsByTagName("a") 语句的计时器结果,或者至少查看访问每个索引的计时器结果,而不是每次查看位置.Add(),可能会更有趣。