Параллельный алгоритм транспонирования матрицы си
Нужно реализовать параллельный алгоритм транспонирования матрицы на потоках. При реализации каждому потоку выделяется некоторое количество строк, которые он должен транспонировать в новый массив. Проблема заключается в том, что этот алгоритм работает намного медленне обычного - наивного (на матрице 10000*5000 наивный - 620мс, параллельный 2553мс), есть подозрение что потоки записывают данные в массив поочереди (закомментированный принт внутри функции потока это подтверждает), возможно ли как то это пофиксить или в чем моя проблема.
код вида скопировал - запустил
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <time.h>
#include <pthread.h>
#define code_all_good 0
#define code_error_array -4
typedef struct thread_data
{
const double** matrix;
double* transposed_matrix;
int rows_start;
int rows_end;
int num_colons;
} thread_data;
void* transpose_thread(void* data);
int transposition_parallel(const double** matrix, double* transposed_matrix, int n, int m);
int transposition_consistant(const double** matrix, double*transposed_matrix, int size_n, int size_m);
#define size_n 10000
#define size_m 5000
int main () {
struct timespec mt1, mt2, mt3, mt4;
double** matrix = (double**)malloc(size_n * sizeof(double*));
for (int i = 0; i < size_n; i++) matrix[i] = (double*)malloc(size_m * sizeof(double));
double* transposed_matrix_for_naive = (double*)malloc(size_n * size_m * sizeof(double));
double* transposed_matrix_for_parallel = (double*)malloc(size_n * size_m * sizeof(double));
for (int i = 0; i < size_n; i++) {
for (int j = 0; j < size_m; j++) {
matrix[i][j] = 10*i + j;
}
}
long tt;
clock_gettime(CLOCK_MONOTONIC, &mt1);
int result1 = transposition_consistant((const double**)matrix, transposed_matrix_for_naive, size_n, size_m);
clock_gettime(CLOCK_MONOTONIC, &mt2);
tt=1000000000*(mt2.tv_sec - mt1.tv_sec)+(mt2.tv_nsec - mt1.tv_nsec);
printf("Naive: %ld \n", tt);
printf("%d \n", result1);
clock_gettime(CLOCK_MONOTONIC, &mt3);
int result2 = transposition_parallel((const double**)matrix, transposed_matrix_for_parallel, size_n, size_m);
clock_gettime(CLOCK_MONOTONIC, &mt4);
tt=1000000000*(mt4.tv_sec - mt3.tv_sec)+(mt4.tv_nsec - mt3.tv_nsec);
printf("PARAL: %ld \n", tt);
printf("%d \n", result2);
for (int i = 0; i < size_n; i++) free(matrix[i]);
free(matrix);
free(transposed_matrix_for_parallel);
free(transposed_matrix_for_naive);
}
void* transpose_thread(void* data) {
thread_data thr = *(thread_data*) data;
for (int i=thr.rows_start; i < thr.rows_end; i++) {
for (int j=0; j < thr.num_colons; j++) {
//printf("%d %d \n", i, j);
thr.transposed_matrix[i + thr.num_colons * j] = thr.matrix[i][j];
}
}
return 0;
}
int transposition_parallel(const double** matrix, double* transposed_matrix, int n, int m) {
printf("Hello from parallel\n");
int num_thread = sysconf(_SC_NPROCESSORS_ONLN);
int count_row = n / num_thread;
printf("count row%d \n", count_row);
pthread_t* pthread_started = (pthread_t*)malloc(num_thread * sizeof(pthread_t));
thread_data* data_all_thread = (thread_data*)malloc(num_thread * sizeof(thread_data));
for (int i = 0; i < num_thread; i++) {
if (i == num_thread - 1)
data_all_thread[i].rows_end = n;
else
data_all_thread[i].rows_end = (i + 1) * count_row;
data_all_thread[i].rows_start = i * count_row;
data_all_thread[i].matrix = matrix;
data_all_thread[i].transposed_matrix = transposed_matrix;
data_all_thread[i].num_colons = m;
pthread_create(&pthread_started[i], NULL, transpose_thread, &data_all_thread[i]);
}
for (int i = 0; i < num_thread; ++i) {
pthread_join(pthread_started[i],NULL);
}
free(pthread_started);
free(data_all_thread);
return code_all_good;
}
int transposition_consistant(const double** matrix, double*transposed_matrix, int n, int m) {
if (!matrix || !transposed_matrix)
return code_error_array;
printf("Hello from naive\n");
for (int i = 0; i < size_n; i++) {
for (int j = 0; j < size_m; j++) {
transposed_matrix[i + size_n * j] = matrix[i][j];
}
}
return code_all_good;
}
компилирую на линукс убунту, процессор 4 ядра(пробовал разное количество потоков от 2 до ...) компиляция gcc -pthread -Wall -Werror -Wpedantic stress_tes.c
Ответы (1 шт):
Проблема заключалась в флагах компиляции -fprofile-arcs -ftest-coverage. Без них параллельный алгоритм быстрее в 2 раза на 4 ядерном процессоре на выходных данных 15к*10к.