Я начал использовать некоторые LinkedLists вместо списков в некоторых из моих алгоритмов С#, надеясь ускорить их. Однако я заметил, что они просто чувствовали себя медленнее. Как любой хороший разработчик, я подумал, что я должен проявлять должную осмотрительность и проверять свои чувства. Поэтому я решил сравнить простые циклы.
Я думал, что заполнение наборов некоторыми случайными целыми числами должно быть достаточным. Я запускал этот код в режиме Debug, чтобы избежать оптимизации компилятора. Вот код, который я использовал:
var rand = new Random(Environment.TickCount);
var ll = new LinkedList<int>();
var list = new List<int>();
int count = 20000000;
BenchmarkTimer.Start("Linked List Insert");
for (int x = 0; x < count; ++x)
ll.AddFirst(rand.Next(int.MaxValue));
BenchmarkTimer.StopAndOutput();
BenchmarkTimer.Start("List Insert");
for (int x = 0; x < count; ++x)
list.Add(rand.Next(int.MaxValue));
BenchmarkTimer.StopAndOutput();
int y = 0;
BenchmarkTimer.Start("Linked List Iterate");
foreach (var i in ll)
++y; //some atomic operation;
BenchmarkTimer.StopAndOutput();
int z = 0;
BenchmarkTimer.Start("List Iterate");
foreach (var i in list)
++z; //some atomic operation;
BenchmarkTimer.StopAndOutput();
Здесь выводится:
Linked List Insert: 8959.808 ms
List Insert: 845.856 ms
Linked List Iterate: 203.632 ms
List Iterate: 125.312 ms
Этот результат сбил меня с толку. Вставка связанного списка должна быть O (1), тогда как в качестве вставки списка Θ (1), O (n) (из-за копирования), если ее необходимо изменить. Итерации обоих списков должны быть O (1) из-за перечислителя. Я посмотрел на разобранный вывод, и он не проливает много света на ситуацию.
У кого-нибудь еще есть мысли о том, почему это так? Я пропустил что-то явно очевидное?
Примечание: здесь приведен пример простого класса BenchmarkTimer: http://procbits.com/2010/08/25/benchmarking-c-apps-algorithms/