пардонте, тут будет некоторый поток сознания;-(
Есть многомерный массив, причем он может быть устроен достаточно сложным образом. Нам нужно взять каждый элемент этого массива и что то с ним сделать с учетом его соседей. Соседи расположены по некоторому шаблону, скажем ближайшие соседи (слева/справа/сверху/снизу) это шаблон «крест», бывают и более сложные шаблоны. Это называется stencil-вычислениями и условно половина числодробилок именно таким вот и занимается.
Для обхода сложных контейнеров обычно используют итераторы, но это весьма криво параллелится средствами OpenMP (во всяком случае я не знаю прямых путей - может они и есть конечно). В новых плюсах есть какие то средства для такого, но они мутные и говорить я о них тут не хочу - хотя бы потому что на многие кластеры эти новые плюсы пока не завезли.
Уже хочется наконец какой то вменяемой концепции для решения таких задач. Пока что придумалось следующее.
-
Есть счетчик size_t i (потому что OpenMP), все ячейки массива пронумерованы подряд.
-
Доступ к элементу массива по такому счетчику это дорого. Поэтому есть некий недоитератор I (я не знаю как это называется правильно), который может быть настроен на основе i. Если все сделано прямыми руками, то небольшие изменения i как правило приводят к минимальной перестройке I. Тогда можно написать что то вроде
#pragma omp parallel for firstprivate(I)
for(size_t i=0; i<N; i++){
I.update(i);
...
}
Будучи настроенным, I ведет себя как умный указатель, то есть у него перегружены операции -> и *. М.б. его и инкрементировать можно - тогда это обычный итератор, но с возможностью update(i). Кроме того I может давать кучу дополнительной информации (координаты ячейки например), и тогда это уже больше чем итератор получается?
- Есть шаблон S[n], это набор из n смещений относительно обрабатываемого элемента массива. Шаблон известен заранее, поэтому можно сгенерировать заранее массив каких то структур, которые относительно малой кровью позволяют от I обращаться к его соседям. Для обычного многомерного массива (который эмулируется на основе одномерного массива + адресная арифметика) это будут просто целые числа, в более сложных случаях это может быть что то выморочное. Есть нюанс - при обращении к соседям надо как то обрабатывать выход за границы многомерного массива.
С обработкой границ все сложно и это уже сильно зависит от того что именно за многомерный массив и что именно мы делаем при выходе за границу. По крайней мере можно рассмотреть два подхода:
-
I.get_nb(S[k]) — принимает какой то элемент шаблона, возвращает указатель на соседнюю ячейку или nullptr. Хотя это кривовато - для анализа выхода за границу может понадобиться много операций, кроме того nullptr может оказаться недостаточно, иногда хочется унать куда именно (налево/направо) мы вылетели.
-
I.get_nb(k) — принимает номер элемента шаблона, возвращает указатель на соседнюю ячейку или nullptr. Это чуть лучше, поскольку шаблон известен заранее, можно сделать маски какой элемент шаблона куда сдвинут.
Наверное можно и каких то методов навешать на I что бы понимать куда относительно границы мы попали…
Вопрос - как правильно называются I? Как правильно называются S (если называются)? И как вообще Ъ делают такие вещи в Ъ программировании?;-)