Если интересно, прошу.
Что?
Если вы смотрели предыдущую статью, то заметили как "мастерски" мы раздавали всем процессам блоки исходной матрицы, но как быть, если мы хотим осуществить обратный процесс, а именно сбор матрицы из блоков. Все как и прежде, ведущим процессом будет нулевой, т.е. именно он получит исходную матрицу.
Как?
Вопрос за вопросом, Да, мы имеем в распоряжении MPI_Gather и MPI_Gatherv и конечно же MPI_Type_vector в сочетании с MPI_Type_commit, но увы, или я не понял почему наш blockType (см. предыдущую статью) не сработал корректно, или может MPI_Gather* не тянет подобное?
Почему бы нет?
Исходя из того, что MPI_Gather* основан на тех же MPI_Send + MPI_Recv + обработка массивов, то почему бы не сделать что-то специализированное, но свое? На этом я и остановился. А именно, на входе блок и коммуникатор, а на выходе матрица, но только у ведущего. Как обычно, давайте сразу смотреть по порядку и параллельно:
int MPI_Gather_block(MATRIX block, MATRIX result, MPI_Comm comm) { int i, j, n, rank, ret, numberOfProcesses; MATRIX *allBlocks; // все блоки у ведущего от всех процессов MPI_Request *requests; // асинхронные запросы на получение блоков // как обычно MPI_Comm_rank(MPI_COMM_WORLD, &rank); // номер процесса MPI_Comm_size(MPI_COMM_WORLD, &numberOfProcesses); // кол-во рабочих процессов // если ведущий, подготовимся к получению всех блоков if (rank == 0) { // ссылки на блоки, блоков у нас столько же сколько и процессов allBlocks = (MATRIX*)malloc(numberOfProcesses * sizeof(MATRIX)); if (allBlocks == NULL) { // видимо маловато памяти return MATRIX_ERROR_NOT_ENOUGH_MEMORY; } // аналогично кол-ву блоков, столько же и запросов requests = (MPI_Request*)malloc(numberOfProcesses * block->rows * sizeof(MPI_Request)); if (requests == NULL) { free(allBlocks); // ну очень мало памяти... return MATRIX_ERROR_NOT_ENOUGH_MEMORY; } // бегаем по всем блокам и создаем блоки-матрицы for (i = 0; i < numberOfProcesses; i++) { ret = createMatrix(&allBlocks[i], block->columns, block->rows); if (ret != MATRIX_SUCCESS) { // ну что можно сказать, без проверок никуда for (j = i; j >= 0; j--) { freeMatrix(&allBlocks[j]); } free(allBlocks); free(requests); return ret; } // запрашиваем блок с соответствующим номером процесса, разумеется асинхронно for (j = 0; j < block->rows; j++) { MPI_Irecv(&allBlocks[i]->items[j * block->columns], block->columns, MATRIX_MPI_TYPE, MPI_ANY_SOURCE, i * block->rows + j, MPI_COMM_WORLD, &requests[i * block->rows + j]); } } } // если интересно что за блок процесс отправляет // printf("%d: ", rank); printMatrix(block, "B"); // шлем блок, но строками, не все сразу for (i = 0; i < block->rows; i++) { MPI_Send(&block->items[i * block->columns], block->columns, MATRIX_MPI_TYPE, 0, rank * block->rows + i, MPI_COMM_WORLD); } // если ведущий, то ожидаем все запросы на блоки if (rank == 0) { MPI_Waitall(numberOfProcesses * block->rows, requests, MPI_STATUSES_IGNORE); free(requests); // поверьте, то что дальше, было высчитано всего за какие то часы // "ничего особенного" здесь нет, просто строку блока с номером процесса // копируем в соответствующее место в результирующей матрице for (i = 0; i < result->columns / block->columns; i++) { for (j = 0; j < result->rows / block->rows; j++) { for (n = 0; n < block->rows; n++) { memcpy(&result->items[((i * block->columns + j) % block->rows) * block->columns + ((i * block->columns + j) / block->rows) * block->rows * result->columns + n * result->columns], &allBlocks[i * block->columns + j]->items[n * block->columns], block->columns * sizeof(MATRIX_TYPE)); } } } // сначала удаляем все матрицы for (i = 0; i < numberOfProcesses; i++) { freeMatrix(&allBlocks[i]); } // а затем и память выделенную под ссылки на матрицы free(allBlocks); } // ура, успех return MATRIX_SUCCESS; }Как то так, разумеется найти ошибку здесь просто, но вроде то, что я решал, решал успешно.
Спасибо и до встречи, как только что-то интересное будет, напишу.
No comments:
Post a Comment