Я пытался оптимизировать хранилище данных в памяти, которое я пишу для своего проекта, и пытался переписать ядро хранения и использовать фиксированные байтовые структуры, но производительность сильно упала, поэтому я хотел спросить, какой подход следует придерживаться?
Этот вопрос связан с этой оптимизацией итераций большого набора данных C # - внешний код в профилировщике и странное поведение , где у меня есть несколько словарей, каждый из которых содержит ровно 1 миллион 100-байтовых массивов, которые имеют определенную структуру для хранения столбца данные.
Существует объект "Осколок", который имеет следующие внутренние хранилища:
private Dictionary<Guid, int> innerCache;
private DataBytes[] innerValues;
private int[] vacancies;
Настроить таким образом, чтобы Guid - Int Dictionary служил для быстрого поиска Guid, а INT Value указывает на индекс массива innerValues.
Вакансии - это индексы, которые были удалены из Словаря и «обнулены» в массиве, и в случае добавления новых записей они будут заполнены новыми данными вместо добавления данных в конец массива.
структура, которую он содержит, очень проста:
public unsafe struct DataBytes
{
public fixed byte bytes[eMemshard.buffer_size];
}
Теперь первый байт этого массива фактически идентифицирует тип записи, представьте его как идентификатор таблицы в стандартных базах данных. На самом деле это также указатель на определение таблицы и столбца, который затем будет использоваться для перевода извлеченных данных в прокси-объекты.
Теперь я перебираю структурный массив innerValues, и мне нужно только посмотреть, больше ли первый байт, чем ноль:
public unsafe void ForEach(Action<DataBytes, byte> act, byte tableIndex)
{
cacheLock.EnterReadLock();
try
{
int l = innerValues.Length;
DataBytes iv;
for (var i = l - 1; i >= 0; i -= 2)
{
iv = innerValues[i];
if (iv.bytes[0] > 0)
{
}
if (i - 1 < 0)
continue;
iv = innerValues[i - 1];
if (iv.bytes[0] > 0)
{
}
}
}
finally
{
cacheLock.ExitReadLock();
}
}
Этот код требует около 200 мсек для выполнения всех 10 миллионов записей, НО, когда я удаляю условия, время падает до 0,4 мс.
Когда я использовал стандартные управляемые массивы, время составляло всего около 24 мс против 0 х мс. Является ли использование фиксированных массивов плохой идеей для этого? Кроме того, есть ли что-то, что могло бы ускорить получение первого байта? Я хочу исключить и пропустить записи, которые находятся в другой «таблице».
Примечание: я знаю, что есть готовые решения для хранения данных в памяти, этот вопрос только для моего собственного исследования, своего рода личный вызов :), но я полагаю, что слишком рано ударил о стену.
Примечание 2: Код на самом деле находится в .NETCore, но я считаю, что это не имеет большого значения.
Edit1:
Чтобы помочь другим, есть причины, по которым некоторые из них, на мой взгляд, не имеют смысла на первый взгляд:
Почему я использовал словарь и массив? Почему бы не использовать только Dictionary? - Итерации по массиву намного быстрее, чем перечисление Dictionary и итерация по его элементам.
Почему я считаю 0 для цикла? - Сравнение с нулем имеет преимущества в производительности по сравнению с очень большим числом.
Почему у меня есть несколько инструкций для каждой итерации и уменьшается на 2? - Очевидно, это как-то связано с конфликтом памяти, и я обнаружил, что он примерно на 10% быстрее, чем итерации одной инструкции. Может быть, кто-то может пролить больше света на это. Использование более двух инструкций в итерации также привело к увеличению производительности, но это было незначительным. Похоже, что при большем количестве команд на итерацию прирост производительности является логарифмическим. Два, кажется, приятное место между повышением производительности и необходимостью иметь дело с оставшимися записями, которые не вписываются в шаг.
Почему у меня объявлены переменные вне цикла? - GC это нравится :) Коллекции GC можно удалить таким образом.