Assume You have a List
public List<T> GetItems1<T>(List<T> source, int offset, int count) { List<T> ret = new List<T>(); for (int i = offset; i < offset + count; i++) { ret.Add(source[i]); } return ret; }
public List<T> GetItems2<T>(List<T> source, int offset, int count) { List<T> ret = source.Skip(offset).Take(count).ToList(); return ret; }
What do You think, which performs better? You may say the second one of course. The first one indexes the source list and calls Add() method count times. The second simply enumerates once till offset then returns count items as a new List
But I was wrong!
The second implementation always slower. The magnitude depends on the offset value but it is always slower!
offset | GetItems1 | GetItems2 |
---|---|---|
0 | 43 | 65 |
10000 | 59 | 729 |
100000 | 44 | 5162 |
1000000 | 42 | 52057 |
3000000 | 44 | 147608 |
The reason is inside the implemetation details of List
The code which I used for result above:
[TestClass] public class Test { private int cycles = 100; private int offset = 0; private int count = 100; [TestMethod] public void perftest1() { var l = GetTestData(); var sw = new Stopwatch(); double r = 0; for (int i = 0; i < cycles; i++) { sw.Reset(); sw.Start(); var result = GetItems1(l, offset, count); sw.Stop(); r += sw.ElapsedTicks; } Assert.Fail((r / cycles).ToString()); } [TestMethod] public void perftest2() { var l = GetTestData(); var sw = new Stopwatch(); double r = 0; for (int i = 0; i < cycles; i++) { sw.Reset(); sw.Start(); var result = GetItems2(l, offset, count); sw.Stop(); r += sw.ElapsedTicks; } Assert.Fail((r / cycles).ToString()); } public List<T> GetItems1<T>(List<T> source, int offset, int count) { ... } public List<T> GetItems2<T>(List<T> source, int offset, int count) { ... } private List<int> GetTestData() { List<int> ret = new List<int>(); for (int i = 0; i < 5000000; i++) { ret.Add(i); } return ret; } }