Сбор списка, выполнение первого действия - PullRequest
0 голосов
/ 25 сентября 2018

Я изучаю производительность коллекций, я заметил странное поведение 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));
        }
    }

И вызвать его дважды в основном методе с различными индексами: enter image description here

Ответы [ 2 ]

0 голосов
/ 25 сентября 2018

Скорость зависит от метода IndexOf, а секундомер соединен при первом обращении к нему.Вы можете изменить свой код так, чтобы вызовы этих методов выходили за пределы цикла хотя бы один раз, как только вы это сделаете, чтобы все записанные времена доступа были согласованы.

Полный 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-компиляция и .NET и Компиляция MSIL в собственный код

0 голосов
/ 25 сентября 2018

Может быть, он выделяет новый (начальный) массив для резервного хранилища.IIRC (и это было давно), начальный размер по умолчанию для резервного хранилища списка составляет 10 элементов.Попробуйте выполнить цикл 11 раз вместо 10 и посмотрите, займет ли следующий доступ больше времени, так как ему нужно выделить новый массив и скопировать элементы за сценой.
- Не берите в голову этот;перечитайте код, и вы проверяете доступ, а не вставляете. -

Возможно, вы также видите артефакт процесса JIT.Если вы перенесете это в отдельный метод и вызовете метод более одного раза, вы увидите похожие результаты в обоих вызовах?

Наконец, может быть более интересно увидеть результаты таймера для операторов .Add(),или, по крайней мере, результаты таймера для доступа к каждому индексу, а не позиции 0 каждый раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...