LINQ が行うことを複製する Enumerable を実装しようとしてOrderBy
いました。そのために、次の 2 つの方法を使用しました。
- Mono の Linq/Enumerable.cs をベースとして使用し、
OrderBy
コードを Enumerable に複製しました。 - 逆コンパイラを使用して .NET のバージョンの印象をつかみ、そのコードを複製しました。
LINQ バージョンと両方のオプションのベンチマークを行うと.Take(10)
(以前は出力が少なかった)、linq バージョンは大幅に高速です (1900 ミリ秒と 3700 ミリ秒)。ソース データはList<MyObject>
で、char メンバで並べ替えられました。最適化build flag was on
。
誰かがこの違いがどこから来るのか説明してもらえますか?
編集: 2. のコードの概要を以下に示します。
ここでBuffer<TElement>
は、 のコピーSystem.Linq.Buffer<TElement>
(ILSpy
内部にあるため からコピー) であり、Sort
(ほとんど) から同じ方法でコピーされSystem.Linq.EnumerableSorter<TElement>
ます。
public class Query2F
{
private Func<Lineitem, char> keySelector;
private char[] keys;
private IComparer<char> comparer;
private bool descending;
public Query2F(Func<Lineitem, char> keySelector, bool descending)
{
this.keySelector = keySelector;
this.descending = descending;
this.comparer = Comparer<char>.Default;
}
public static IEnumerable<Lineitem> Execute(List<Lineitem> lineitem)
{
Buffer<Lineitem> buffer = new Buffer<Lineitem>(lineitem);
if (buffer.count > 0)
{
Query2F q2f = new Query2F((s => s.returnflag), false);
int[] array = q2f.Sort(buffer.items, buffer.count);
q2f = null;
for (int i = 0; i < buffer.count; i++)
{
yield return buffer.items[array[i]];
}
}
yield break;
}
private void ComputeKeys(Lineitem[] elements, int count)
{
this.keys = new char[count];
for (int i = 0; i < count; i++)
{
//this.keys[i] = this.keySelector(elements[i]);
this.keys[i] = elements[i].returnflag;
}
}
private int CompareKeys(int index1, int index2)
{
int num = this.comparer.Compare(this.keys[index1], this.keys[index2]);
if (num == 0)
{
return index1 - index2;
}
else
{
if (!this.descending)
{
return num;
}
return -num;
}
}
internal int[] Sort(Lineitem[] elements, int count)
{
this.ComputeKeys(elements, count);
int[] array = new int[count];
for (int i = 0; i < count; i++)
{
array[i] = i;
}
this.QuickSort(array, 0, count - 1);
return array;
}
private void QuickSort(int[] map, int left, int right)
{
do
{
int num = left;
int num2 = right;
int index = map[num + (num2 - num >> 1)];
do
{
if (num < map.Length)
{
if (this.CompareKeys(index, map[num]) > 0)
{
num++;
continue;
}
}
while (num2 >= 0 && this.CompareKeys(index, map[num2]) < 0)
{
num2--;
}
if (num > num2)
{
break;
}
if (num < num2)
{
int num3 = map[num];
map[num] = map[num2];
map[num2] = num3;
}
num++;
num2--;
}
while (num <= num2);
if (num2 - left <= right - num)
{
if (left < num2)
{
this.QuickSort(map, left, num2);
}
left = num;
}
else
{
if (num < right)
{
this.QuickSort(map, num, right);
}
right = num2;
}
}
while (left < right);
}
}
internal struct Buffer<TElement>
{
internal TElement[] items;
internal int count;
internal Buffer(IEnumerable<TElement> source)
{
TElement[] array = null;
int num = 0;
ICollection<TElement> collection = source as ICollection<TElement>;
if (collection != null)
{
num = collection.Count;
if (num > 0)
{
array = new TElement[num];
collection.CopyTo(array, 0);
}
}
else
{
foreach (TElement current in source)
{
if (array == null)
{
array = new TElement[4];
}
else
{
if (array.Length == num)
{
TElement[] array2 = new TElement[checked(num * 2)];
Array.Copy(array, 0, array2, 0, num);
array = array2;
}
}
array[num] = current;
num++;
}
}
this.items = array;
this.count = num;
}
internal TElement[] ToArray()
{
if (this.count == 0)
{
return new TElement[0];
}
if (this.items.Length == this.count)
{
return this.items;
}
TElement[] array = new TElement[this.count];
Array.Copy(this.items, 0, array, 0, this.count);
return array;
}
}