У меня есть алгоритм, написанный на C, который обрабатывает пару двумерных массивов (например, с размером Y x X) для создания другого двумерного массива того же размера. Все три массива содержат 32-битные поплавки и имеют одинаковый размер Y x X, где Y может составлять несколько десятков, а X - около миллиона.Как я могу уменьшить влияние производительности на транспонированный порядок доступа к массиву?
К сожалению:
- все массивы должны быть в row-major order (сканирование через X получает доступ к непрерывной памяти),
- алгоритм требует, чтобы внутренний цикл для сканирования по Y размерности.
Возможно, неудивительно, что доступ к данным в этом несмежном режиме относительно медленный. Итак ...
Что я могу сделать для уменьшения влияния производительности на несмежные обращения к памяти?
(.. NB Это был long shot, но я пробовал различные образцы инструкций предвыборки, чтобы принести в наступающих колонн, но все безрезультатно)
Следующая (обновлена) код демонстрирует проблему:
#include <stdio.h>
#include <stdlib.h>
#define NX 1000000
#define NY 30
int main() {
float *a = malloc(sizeof(float) * NY * NX);
float *b = malloc(sizeof(float) * NY * NX);
float *c = malloc(sizeof(float) * NY * NX);
size_t y, x, offset;
float v;
for(x=0; x<NX; x++) {
v = 1;
for(y=0; y<NY; y++) {
offset = x + NX * y;
if(a[offset] < 0) {
v = 2;
}
c[offset] = v * b[offset];
}
}
free(a);
free(b);
free(c);
}
На тестовой машине с процессором E5520 с частотой 2,27 ГГц это занимает ~ 1 с для выполнения, хотя это только чтение ~ 220 МБ и запись ~ 110 МБ.
Если вы можете разбить свои данные до чанк/блоков с резонансными размерами, которые позволяют ему совместно использовать сквозные двойные петли в кеше, это может помочь. Это зависит, конечно, от того, как ваш алгоритм (и проблема) может объединить результирующие блоки. В простой вещи, как дополнение выше, это не требует дальнейшей работы. –
Зависит от того, что такое тело цикла, и какие зависимости между итерациями. Лучше всего переключиться на порядок циклов, но может потребоваться некоторое вспомогательное хранилище.Алгоритм с заблокированным/разбитым слоем будет хорошим вариантом, но вы должны быть осторожны в отношении размера плитки с учетом конкретных параметров вашего компьютера (т. Е. Размер плитки, который хорошо работает в кеше 3 МБ, может работать не так хорошо, когда вы запускается в системе с 512 КБ кеша). Я думаю, нам нужна дополнительная информация о вашем фактическом алгоритме, а не тестовом примере игры выше ... – twalberg
Я обновил код примера, чтобы попытаться проиллюстрировать характер обработки порядка столбцов. –