Подтвердить что ты не робот

Существует ли какое-либо обходное решение для "резервирования" доли кеша?

Предположим, что я должен написать вычислительную интенсивную функцию C или С++, которая имеет 2 массива в качестве входных данных и один массив в качестве вывода. Если вычисление использует 2 входных массива чаще, чем обновление выходного массива, я попаду в ситуацию, когда выходной массив редко кэшируется, потому что он выведен для извлечения 2 входных массивов.

Я хочу зарезервировать одну часть кэша для выходного массива и как-то принудительно обеспечить, чтобы эти строки не удалялись после их получения, , чтобы всегда записывать частичные результаты в кеше.

Update1(output[]) // Output gets cached
DoCompute1(input1[]); // Input 1 gets cached
DoCompute2(input2[]); // Input 2 gets cached
Update2(output[]); // Output is not in the cache anymore and has to get cached again
...

Я знаю, что существуют механизмы, помогающие выселению: clflush, clevict, _mm_clevict и т.д. Существуют ли какие-либо механизмы для противоположного?

Я думаю о трех возможных решениях:

  • С помощью _mm_prefetch время от времени извлекать данные, если они были выселены. Однако это может привести к излишнему трафику плюс, что мне нужно быть очень осторожным, когда их вводить;
  • Попытка выполнить обработку на небольших кусках данных. Однако это будет работать только в том случае, если проблема позволяет это;
  • Отключение аппаратных префетов, где это возможно, чтобы уменьшить скорость нежелательных выселений.

Кроме этого, есть ли элегантное решение?

4b9b3361

Ответ 1

Процессоры Intel имеют что-то, что называется No Eviction Mode (NEM), но я сомневаюсь, что это то, что вам нужно.

Пока вы пытаетесь оптимизировать вторую (ненужную) выборку вывода [], вы подумали о том, чтобы использовать регистры SSE2/3/4 для хранения промежуточных выходных значений, обновлять их при необходимости и записывать их только тогда, когда все обновления, относящиеся к этой части выхода [], выполнены? Я сделал что-то подобное при вычислении FFT (Fast Fourier Transforms), где часть вывода находится в регистрах, и они перемещаются (в память) только тогда, когда известно, что они больше не будут доступны. До тех пор все обновления происходят с регистрами. Для эффективного использования регистров SSE * вам необходимо ввести встроенную сборку. Конечно, такие оптимизации сильно зависят от характера алгоритма и размещения данных.

Ответ 2

Я пытаюсь лучше понять вопрос:

Если верно, что массив "output" строго предназначен для вывода, и вы никогда не будете делать что-то вроде

output[i] = Foo(newVal, output[i]);

тогда все элементы в выходе [] строго записываются. Если это так, все, что вам нужно было бы "зарезервировать", это одна линия кеша. Не правда ли?

В этом сценарии все записи в 'output' генерируют кеш-заливки и могут конкурировать с кэшами, необходимыми для массивов 'input'.

Разве вы не хотите, чтобы ограничение на вывод кешлайн могло потребляться в отличие от резервирования определенного количества строк.

Ответ 3

Я вижу два варианта, которые могут работать или не работать в зависимости от того, какой вы нацеливаете CPU, и о точном потоке программы:

  • Если output записывается только и не читается, вы можете использовать потоковые хранилища, т.е. команду записи с подсказкой без чтения, поэтому она не будет выбрана в кеш.

  • Вы можете использовать предварительную выборку с подсказкой, не привязанной по времени (NTA) для input. Я не знаю, как это реализовано в целом, но я точно знаю, что на некоторых процессорах Intel (например, Xeon Phi) каждый аппаратный поток использует определенный способ кеша для данных NTA, то есть с 8-сторонним кешем 1/8th на поток.

Ответ 4

Я предполагаю, что решение этого скрыто внутри, используемый алгоритм и размер кеша L1 и размер строки кеша. Хотя я не уверен, насколько мы будем улучшать производительность.

Мы можем, вероятно, ввести искусственные чтения, которые умело уклоняются от компилятора и во время выполнения, также не мешают вычислениям. Одно искусственное чтение должно заполнять строки кеша, сколько необходимо для размещения одной страницы. Поэтому алгоритм должен быть модифицирован для вычисления блоков выходного массива. Что-то вроде тех, которые используются при матричном умножении огромных матриц, выполняемых с использованием графических процессоров. Они используют блоки матриц для вычисления и записи результата.

Как указывалось ранее, запись в выходной массив должна произойти в потоке.

Чтобы ввести искусственное чтение, мы должны инициализировать во время компиляции выходной массив в правильных местах, один раз в каждом блоке, возможно с 0 или 1.