C # память хранилища данных - фиксированные байтовые массивы или «безопасный» байтовый массив? - PullRequest
0 голосов
/ 06 сентября 2018

Я пытался оптимизировать хранилище данных в памяти, которое я пишу для своего проекта, и пытался переписать ядро ​​хранения и использовать фиксированные байтовые структуры, но производительность сильно упала, поэтому я хотел спросить, какой подход следует придерживаться? Этот вопрос связан с этой оптимизацией итераций большого набора данных 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 можно удалить таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...