Эффективный доступ к 2 массивам структур в цикле - PullRequest
0 голосов
/ 10 апреля 2019

У меня есть следующие 2 массива структур и контейнерный класс:

[Serializable]
public struct Pointer {

    public byte State;

}

[Serializable]
public struct Data {

    public uint Hash;
    public byte SomeIndex;
    public byte SomeMoreIndex;
    public byte SomeFurtherIndex;

}

[Serializable]
public class Grid {

    public Pointer[] Cells;
    public Data[] CellData;

}

И я намереваюсь зациклить их следующим образом:

int index = 0;
for (var i = 0; i < Cells.Length; i++) {
    if (Cells[i] != 0) {
        // access CellData[index], and do more work
        index++;
    }
}

Я знаю, как потеря кэша ЦП влияет на производительность на базовом уровне, поэтому я пытаюсь получить доступ к этим двум массивам по порядку. Но мои вопросы:

  • Поскольку мы обращаемся к двум массивам с чередованием: Обнуляет ли это преимущество в производительности при последовательном доступе к памяти?
  • Если нет, то как кэш процессора работает в подобных случаях?
  • Что если внутри цикла после чтения CellData[index] я использую его Hash для доступа к Dictionary<Hash, ItemClass>, это еще больше усложнит работу самого цикла?
  • Я решил разделить 1 структуру на 2, чтобы сэкономить память (и я мог бы использовать byte[] вместо Pointer[]), поскольку сетка может быть довольно большой и потенциально разреженной, это справедливый компромисс?

1 Ответ

0 голосов
/ 12 апреля 2019

Элементы в той же строке 64B по-прежнему будут иметь преимущества кэширования, если повторение будет достаточно быстрым (т. Е. «Больше работы» не перебивает кэш).

Элементы между строками должны по-прежнему пользоваться преимуществами предварительной выборки HW, если массивы находятся на разных страницах.

Использование поля Hash создаст зависимость от данных и понесет штраф, конечно. Это обычная проблема A[B[i]], и некоторые академические предварительные сборщики ее решают (например, IMP ), но, насколько мне известно, ничего в коммерческих процессорах нет. Существующая «последовательная» предварительная выборка HW должна уменьшить большую ее часть, если она выполняется достаточно далеко вперед, чтобы предварительно выбрать достаточное количество итераций для хэширования, прежде чем они фактически будут использованы, и в этом случае штраф будет уменьшен до двух последовательных обращений L1 (или любого кеша). Уровень реализует этот prefetcher - обычно L1 должен иметь один). Обратите внимание, что влияние на производительность не является прямым, так как разные итерации независимы, но задержка памяти будет переводиться в ограничение BW памяти после насыщения буферов обработки ошибок.

...