В настоящее время я портирую реализацию дерева критических битов с C на C#, используя небезопасный C#. Я заметил какое-то странное поведение с точки зрения производительности, которое я не могу объяснить.
Допустим, у меня есть узел дерева с ровно двумя дочерними элементами. Поскольку у меня не может быть небезопасной структуры с массивом указателей, я бы сделал что-то вроде этого:
[StructLayout(LayoutKind.Sequential, Pack = 0)]
internal unsafe struct CritBitTreeNode
{
public byte Type;
public CritBitTreeNode* Child1;
public CritBitTreeNode* Child2;
public int Byte;
public byte Otherbits;
}
В зависимости от расчетного направления, я хочу следовать Child1 или Child2 в al oop. Таким образом, код выглядит примерно так:
while (node->Type == 1)
{
int direction = CalculateDirection(...)
node = direction == 0 ? node->Child1 : node->Child2;
}
Теперь я подумал, что иметь доступ к элементу if и затем должно быть медленнее, чем просто захватить указатель и добавить к нему направление следующим образом:
node = *(&node->Child1 + direction)
Оказывается, мое предположение было неверным. Мои тесты сравнивают мой алгоритм с хэш-набором по умолчанию. Изменение этой единственной строки приводит к изменению соотношения с довольно хорошего 0,75 до довольно плохого 1,33 (базовый хэшсет содержит). Может кто-нибудь объяснить мне, почему арифметика указателя c кажется медленнее?
Полный исходный код можно найти здесь (метод Contains): https://github.com/TimHeinrich/CritBitTree/blob/master/CritBitTree/UnmanagedCritBitTree.cs
Я также пытался воспроизвести эту проблему с помощью микробенчмарка, но безуспешно, арифметика указателей там выигрывает, поэтому я полагаю, что это должно быть что-то совершенно иное, например, условие ветвления? Любое понимание этого очень ценится!