Я хотел бы сгенерировать случайную матрицу с OpenMP, как если бы она была сгенерирована последовательной программой, то есть, если какой-либо генератор последовательных матриц выдает мне матрицу, подобную следующей:
1.0 2.0 3.0 4.0
5.0 6.0 7.0 8.0
9.0 0.0 1.0 2.0
3.0 4.0 5.0 6.0
Я хочу параллельная версия OpenMP той же программы для генерации одной и той же матрицы без чередующихся строк .
Вот как я постепенно подошел к проблеме.
Учитывая мой сериал функция генератора C, генерирующая матрицу в виде одномерного массива:
void generate_matrix_array(
double *v,
int rows,
int columns,
double min,
double max,
int seed
) {
srand(seed);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
v[i*rows + j] = min + (rand() / (RAND_MAX / (max - min)));
}
}
}
Сначала я наивно попробовал директиву #pragma omp parallel for
для внешнего цикла for
; тем не менее, нет никакой гарантии в отношении порядка строк, поскольку выполнение потоков чередуется, поэтому они генерируются в недетерминированном порядке c.
Добавление опции ordered
решит проблему по цене создания бесполезная многопоточность в данном конкретном случае.
Чтобы решить эту проблему, я попытался разбить вручную матричный массив так, чтобы поток i генерировал i -й его фрагмент:
void generate_matrix_array_par(
double *v,
int rows,
int columns,
double min,
double max,
int seed
) {
srand(seed);
#pragma omp parallel \
shared(v)
{
int tid = omp_get_thread_num();
int nthreads = omp_get_num_threads();
int rows_per_thread = round(rows / (double) nthreads);
int rem_rows = rows % (nthreads - 1) != 0?
rows % (nthreads - 1):
rows_per_thread;
int local_rows = (tid == 0)?
rows_per_thread:
rem_rows;
int lower_row = tid * local_rows;
int upper_row = ((tid + 1) * local_rows);
printf(
"[T%d] receiving %d of %d rows from row %d to %d\n",
tid,
local_rows,
rows,
lower_row,
upper_row - 1
);
printf("\n");
fflush(stdout);
for (int i = lower_row; i < upper_row; i++) {
for (int j = 0; j < columns; j++) {
v[i*rows + j] = min + (rand() / (RAND_MAX / (max - min)));
}
}
}
}
Однако, несмотря на то, что матричный вектор правильно разделен между потоками, по какой-то неизвестной мне причине каждый поток генерирует свои строки в матрицу за детерминированный порядок c, т.е. если я хочу сгенерировать матрицу 8x8 с 4 потоками, а поток 3 назначен строкам 4 и 5, он будет генерировать две смежные строки в матричном массиве, но каждый раз в неправильной позиции, как если бы я не выполнял никакого разделения, и директива omp parallel for
была на месте.
Я наконец скептически попытался вернуться к наивному подходу b y указав shared(v)
и schedule(static, 16)
опции для директивы omp parallel for
, и это «волшебным образом» работает:
void generate_matrix_array_par(
double *v,
int rows,
int columns,
double min,
double max,
int seed
) {
srand(seed);
int nthreads = omp_get_max_threads();
int chunk_size = (rows * columns) / nthreads;
#pragma omp parallel for \
shared(v) \
schedule(static, chunk_size)
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
v[i*rows + j] = min + (rand() / (RAND_MAX / (max - min)));
}
}
}
Опция schedule
добавляется, так как я читаю где-то еще что он избавляется от конфликтов кэша. Редактировать: Похоже, schedule
разбивает данные на потоки в циклическом порядке в соответствии с заданным размером чанка; поэтому, если я поделюсь потоками размером N / nthreads между потоками, данные будут назначены за один раунд.
Есть вопросы? ДА !!!
Теперь я хотел бы знать, пропустил ли я какое-то соображение по поводу проблемы, или не смог, поскольку я не убежден в справедливости моей последней версии программы, несмотря на то, что это работает.