(Этот вопрос возникает из обсуждения, которое началось здесь)
Я сравнивал тайминги для поиска значения true
в List<bool>
с помощью List.Contains()
с параметрами для ручного проката.
Я вижу разные результаты от сообщений других людей. Я попробовал это на нескольких системах, и цикл кажется более быстрым на 2 и 3,5 раза на всех системах, в которых я его пробовал. Эти системы варьируются от 5-летних ноутбуков с XP с .Net 4 до недавних ПК под управлением Windows 8 и .Net 4.5.
Другие люди сообщают о разных результатах, а именно: List.Contains()
примерно такая же скорость, как или немного быстрее, чем цикл.
Вот мой тестовый код.
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main()
{
int size = 10000000;
int count = 10;
List<bool> data = new List<bool>(size);
for (int i = 0; i < size; ++i)
data.Add(false);
var sw = new Stopwatch();
for (int trial = 0; trial < 5; ++trial)
{
sw.Restart();
for (int i = 0; i < count; ++i)
TestViaLoop(data);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds + " TestViaLoop()");
sw.Restart();
for (int i = 0; i < count; ++i)
TestViaListContains(data);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds + " TestViaListContains()");
Console.WriteLine();
}
}
static bool TestViaLoop(List<bool> data)
{
for (int i = 0; i < data.Count; ++i)
if (data[i])
return true;
return false;
}
static bool TestViaListContains(List<bool> data)
{
return data.Contains(true);
}
}
}
Чтобы протестировать этот код, вы должны скомпилировать его как сборку x86 RELEASE и запустить ее извне отладчика.
Вот мои результаты с моего ПК с Windows 8 x64 с использованием среды .NET 4.5 (хотя я получаю аналогичные результаты с .Net 4):
Times are in milliseconds
126 TestViaLoop()
441 TestViaListContains()
122 TestViaLoop()
428 TestViaListContains()
131 TestViaLoop()
431 TestViaListContains()
138 TestViaLoop()
426 TestViaListContains()
122 TestViaLoop()
439 TestViaListContains()
Как вы можете видеть, цикл занимает около 1/3 времени в моей системе.
Теперь, если мы используем Resharper
для просмотра реализации List.Contains()
, он выглядит так:
bool Contains(T item)
{
if (item == null)
{
for (int j = 0x0; j < this._size; j++)
{
if (this._items[j] == null)
{
return true;
}
}
return false;
}
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0x0; i < this._size; i++)
{
if (comparer.Equals(this._items[i], item))
{
return true;
}
}
return false;
}
Хотя он использует Comparer.Equals()
(который должен сделать его медленнее, чем цикл), он также использует частный массив _items[]
, что позволяет избежать проверки диапазона индекса, который будет использоваться для моей реализации цикла.
У меня есть три вопроса:
- Может ли кто-нибудь повторить результаты, которые я вижу? (Не забудьте запустить сборку релиза вне отладчика.)
- Если да, может ли кто-нибудь объяснить, как мой цикл может быть намного быстрее, чем
List.Contains()
? - Если нет, может ли кто-нибудь объяснить, почему я вижу, что мой цикл будет быстрее?
Это не просто академический интерес для меня, так как я пишу код, который работает с большим количеством числовых данных и который должен быть как можно быстрее, и это то, о чем мне нужно знать. (Примечание: Да, я прорабатываю вещи и только пытаюсь оптимизировать вещи, которые нужно оптимизировать... Я знаю о проблемах преждевременной оптимизации.)
[EDIT]
Мне кажется, что это может быть связано с процессором. Все системы, на которых я это пробовал, имеют процессоры Intel, хотя и очень разные модели от Quad Core на 3,8 ГГц до одного ядра Pentium M на частоте 1,6 ГГц...
Для тех из вас, кто видит, что цикл работает медленнее, вы используете процессоры Intel?