C multithreading slower than single-threading when multiplying matrices - c

I'm using theads in my C code to make the code faster, but it actually makes it worse.
I have a matrix and a matrix_operation class :
struct matrix
{
char *name;
size_t rows;
size_t columns;
double *value;
};
typedef struct matrix_operation matrix_operation;
struct matrix_operation
{
matrix r;
matrix m1;
matrix m2;
size_t row;
};
To multiply the matrices, I use these functions :
matrix matrix_mul(char *name, matrix m1, matrix m2, size_t replace)
{
matrix r = matrix_init(name, m1.rows, m2.columns);
matrix_operation *mat = malloc(sizeof *mat * m1.rows);
pthread_t *th = malloc(sizeof *th * m1.rows);
for (size_t i = 0; i < m1.rows; i++)
{
matrix_operation param = {r, m1, m2, i};
mat[i] = param;
pthread_create(&th[i], NULL , matrix_mul_th, &mat[i]);
}
for (size_t i = 0; i < m1.rows; i++)
{
pthread_join(th[i], NULL);
}
free(mat);
free(th);
if (replace == 1)
matrix_free(m1);
else if (replace == 2)
matrix_free(m2);
else if (replace == 3)
{
matrix_free(m1);
matrix_free(m2);
}
return r;
}
void *matrix_mul_th(void *arg)
{
matrix_operation mat = *(matrix_operation*)arg;
for (size_t j = 0; j < mat.m2.columns; j++)
{
double sum = 0;
for (size_t k = 0; k < mat.m1.columns; k++)
sum += matrix_get(mat.m1,mat.row,k) * matrix_get(mat.m2,k,j);
matrix_put(mat.r,mat.row,j,sum);
}
return NULL;
}
Do you have any idea why the problem may be ? And how to improve the code ?
The matrices are stored as a 1D array.
Thanks a lot for your time,
Lucas

Related

How can I perform Matrix Multiplication without restricting the dimensions of the matrices read from a text file in C programming

I want to multiply matrices but without restricting the dimensions of the matrix A and B that are actually read from different textfiles MatAsmall.txt MatBsmall.txt MatAlarge.txt MatBlarge.txt. There are small matrices and even huge matrices in different text files. I want to create 1 program to read a file of any dimension and then store the dimensions in a variable which will help further with the matrix multiplication,multithreading and dynamic memory allocation. All of the matrices used are 2d.
How can I do that?
Provided that your files look something like:
5 5
-9 8 -8 -3 10
8 -10 10 -8 -4
-2 -8 8 10 8
4 3 5 -7 -7
-5 4 -3 7 3
where 5 and 5 are the dimensions of later defined matrix, you could use such function to read them:
struct matrix_t {
int **ptr;
int width;
int height;
};
struct matrix_t* matrix_create_struct(int width, int height) {
struct matrix_t *matrix = (struct matrix_t *) malloc(sizeof(struct matrix_t));
matrix->ptr = (int **) malloc(height * sizeof(int *));
for (int i = 0; i < height; i++) {
*(matrix->ptr + i) = (int *) malloc(width * sizeof(int));
}
matrix->width = width;
matrix->height = height;
return matrix;
}
struct matrix_t *matrix_load_from_file(const char *filename) {
FILE* fptr = fopen(filename, "rt");
int width, height;
fscanf(fptr, "%d", &width);
fscanf(fptr, "%d", &height);
struct matrix_t *matrix = matrix_create_struct(width, height);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
fscanf(fptr, "%d", (*(matrix->ptr + i) + j));
}
}
fclose(fptr);
return matrix;
}
Here I am using dynamic allocation, since, as you said, we don't know what the dimensions of matrix will be.
And such in order to multiply them:
struct matrix_t* matrix_multiply(const struct matrix_t *m1, const struct matrix_t *m2) {
if (m1->width != m2->height)
return NULL;
struct matrix_t *new_matrix = matrix_create_struct(m2->width, m1->height);
for (int i = 0; i < m1->height; i++) {
for (int j = 0; j < m2->width; j++) {
int res = 0;
for (int k = 0, l = 0; k < m1->width && l < m2->height; k++, l++)
res += *(*(m1->ptr + i) + k) * *(*(m2->ptr + l) + j);
*(*(new_matrix->ptr + i) + j) = res;
}
}
return new_matrix;
}
Here I am using math I looked up here: https://www.mathsisfun.com/algebra/matrix-multiplying.html. I am returning NULL if the following isn't true:
The number of columns of the 1st matrix must equal the number of rows of the 2nd matrix.
Please, note how optimistic I am... Each and every fopen and malloc should be checked if it didn't return NULL, also be careful with fscanfs if you don't have trust for files creators.
I used such code to test my code:
void display_matrix(const struct matrix_t * matrix) {
for (int i = 0; i < matrix->height; i++) {
for (int j = 0; j < matrix->width; j++) {
printf("%d ", *(*(matrix->ptr + i) + j));
}
printf("\n");
}
}
int main() {
struct matrix_t * m1 = matrix_load_from_file("test.txt");
struct matrix_t * m2 = matrix_load_from_file("test.txt");
struct matrix_t * m3 = matrix_multiply(m1, m2);
display_matrix(m3);
return 0;
}
and checked the results here: https://matrixcalc.org/. Everything seems to be working fine, but feel free to ask in case of questions or doubts.

local variable may point to deallocated memory

i have a struct in the header (ex3.h)
typedef struct matrix {
double **data;
size_t n_rows;
size_t n_columns;
} matrix;
in the c file i have function "create_matrix" and another function slice
and i'm getting warning "local variable may point to deallocated memory" in some places.. i mark them as "--->" in comments
#include <stdlib.h>
# include <stdio.h>
# include "ex3.h"
#define DEBUG
matrix *create_matrix (size_t n_rows, size_t n_columns)
{
matrix *mat = malloc (sizeof (matrix));
if (!mat)
{
fprintf (stderr, POINTER_ERROR);
return NULL;
}
if (n_rows == ZERO || n_columns == ZERO)
{ mat->data = NULL, mat->n_rows = ZERO, mat->n_columns = ZERO; }
else
{
{
double **mat_data = malloc (n_rows * sizeof (double *));
if (!mat_data)
{
free (mat);
fprintf (stderr, POINTER_ERROR);
return NULL;
}
for (int i = ZERO; i < n_rows; i++)
{
mat_data[i] = malloc (n_columns * sizeof (double));
if (!mat_data[i])
{
for (int k = ZERO; k < i; k++)
{
free (mat_data[k]);
}
free (mat_data), free (mat);
fprintf (stderr, POINTER_ERROR);
return NULL;
}
else
{
for (int j = ZERO; j < n_columns; j++)
{
mat_data[i][j] = (double) ZERO;
}
}
}
mat->data = mat_data, mat->n_rows = n_rows, mat->n_columns = n_columns;
}
}
return mat;
}
matrix *slice (matrix *mat, size_t row_start,
size_t row_end, size_t col_start, size_t col_end)
{
"some condions..."
{
matrix *m = create_matrix (ZERO, ZERO); //nul matrix
if (!m) "<---"
{
return NULL;
}
return m; "<---"
}
else
{
size_t row = row_end - row_start, col = col_end - col_start;
matrix *new_mat = create_matrix (row, col);
if (!new_mat)"<---"
{
return NULL;
}
for (int i = ZERO; i < row; i++)
{
for (int j = ZERO; j < col; j++)
{
"--->" (new_mat->data)[i][j] = mat->data[i + row_start][j + col_start];
}
}
return new_mat; "<---"
}
}
i made a few conditions that insure i'm not try to go to a space in the memory place that is not part of my matrix, so why clion still make a warning
thanks for trying to help!
I would do it another way. Instead plenty of allocations I would use only one. Instead of negative tests (ie is something is wrong) I would use positive tests and have only one return point from the function.
typedef struct matrix {
size_t n_rows;
size_t n_columns;
double data[];
} matrix;
matrix *allocate(size_t r, size_t c)
{
matrix *m = malloc(sizeof(*m) + r * c * sizeof(m -> data[0]));
if(m)
{
m -> n_rows = r;
m -> n_columns = c;
}
return m;
}
matrix *slice(matrix *m, size_t sr, size_t er, size_t sc, size_t ec)
{
matrix *result = NULL;
size_t lc = ec - sc, lr = er - sr;
if(m && m -> n_rows > er && m -> n_rows > sr && sr <= er && m -> n_columns > ec && m -> n_columns > sc && sc <= ec)
{
result = allocate(lr, lc);
if(result)
{
double (*dest)[lc] = &result -> data;
double (*src)[m -> n_columns] = &m -> data;
result -> n_rows = lr;
result -> n_columns = lc;
for(size_t row = 0; row < lr; row ++)
{
memcpy(dest[row], &src[sr + row][sc], lc * sizeof(m -> data[0]));
}
}
}
return result;
}

CodeSignal problem. I want to complete the code

I have the following problem. The function printMatrix
Receive an matrix for example:
matrix:
[[0,1,1,2],
[0,5,0,0],
[2,0,3,3]]
The code that I must use is the following:
// Definition for arrays:
// typedef struct arr_##name {
// int size;
// type *arr;
// } arr_##name;
//
// arr_##name alloc_arr_##name(int len) {
// arr_##name a = {len, len > 0 ? malloc(sizeof(type) * len) : NULL};
// return a;
// }
//
//
void printMatrix(arr_arr_integer matrix)
{
}
As a clue they give me that the number of columns and rows can be determined in the following way.
int columns = matrix.arr->size; //No.columns
int rows = matrix.size; //No.rows
//Or
int columns = matrix.arr[0].size; //No.columns
int rows = matrix.size; //No.rows
My question lies in how is the rest of the code written so that the previous tracks can work?
That is, for this to work within the function printMatrix
What should you add or modify in your code for the above methods to work?
typedef struct arr_arr_integer {
int size;
type *arr;
} arr_arr_integer;
arr_arr_integer alloc_arr_arr_integer(int len) {
arr_arr_integer a = {len, len > 0 ? malloc(sizeof(type) * len) : NULL};
return a;
}
void printMatrix(arr_arr_integer matrix)
{
int columns = matrix.arr->size; //No.columns
int rows = matrix.size; //No.rows
//print matrix?
}
int main(int argc, char const *argv[])
{
//input matrix?
printMatrix(arr_arr_integer matrix)
return 0;
}
I repeat. I must use this code strictly
int columns = matrix.arr->size; //No.columns
int rows = matrix.size; //No.rows
The problem is that when I try to use those tracks I get the following compilation error.
error: request for member 'size' in something not a structure or union
The function alloc_arr_integer allocates a 1D array of integers.
If you need a 2D array, you'll have to call the function multiple times.
Something like:
arr_integer my2Darray[rows];
// Create the 2D array
for (int i = 0; i < rows; ++i)
{
my2Darray[i] = alloc_arr_integer(columns);
assert(my2Darray[i].arr != NULL);
}
// Initialize the 2D array
for (int i = 0; i < rows; ++i)
{
for (int j = 0; j < columns; ++j)
{
my2Darray[i].arr[j] = i * 1000 + j;
}
}
Putting it together:
int main(void)
{
int rows = 3;
int columns = 5;
arr_integer my2Darray[rows];
// Create the 2D array
for (int i = 0; i < rows; ++i)
{
my2Darray[i] = alloc_arr_integer(columns);
assert(my2Darray[i].arr != NULL);
}
//Initialize the 2D array
for (int i = 0; i < rows; ++i)
{
for (int j = 0; j < columns; ++j)
{
my2Darray[i].arr[j] = (i + 1) * 1000 + j;
}
}
for (int i = 0; i < rows; ++i)
{
for (int j = 0; j < columns; ++j)
{
printf("%d ", my2Darray[i].arr[j]);
}
printf("\n");
}
return 0;
}
OUTPUT
1000 1001 1002 1003 1004
2000 2001 2002 2003 2004
3000 3001 3002 3003 3004
I assume memory allocation for 'matrix' is done somewhere else.
void printMatrix(arr_arr_integer matrix)
{
int rows = matrix.size;
int columns = matrix.arr.size;
int i, j = 0;
/*print array*/
for (i = 0; i < rows; i++)
{
for (j = 0; j < columns; j++)
printf("%d ", a.arr[i].arr[j]);
printf("\n");
}
}
I was practicing for code signal and I wrote my own functions to stablish the arrays. They are as shown below (even though this is a little bit too late maybe).
typedef struct arr_integer
{
int size;
int *arr;
} arr_integer;
typedef struct arr_arr_integer
{
int size;
arr_integer *arr;
} arr_arr_integer;
arr_integer alloc_arr_integer(int size)
{
arr_integer *pointer;
pointer = malloc(sizeof(arr_integer));
pointer->size = size;
pointer->arr = malloc(size * sizeof(int));
return *pointer;
}
arr_arr_integer alloc_arr_arr_integer(int size)
{
arr_arr_integer *pointer;
pointer = malloc(sizeof(arr_arr_integer));
pointer->size = size;
pointer->arr = malloc(size * sizeof(arr_integer));
for (int i = 0; i < size; i++)
{
pointer->arr[i].arr = malloc(size * sizeof(int));
}
return *pointer;
}

Can't allocate memory for triple pointer

I'm doing a project for school and I have a problem and I can't figure out how to solve it. I'm trying to allocate memory for a triple pointer in a function that I will be able to use it as a 2D array, save data and then use it in a different function. But for some reason I can't use the data once I'm out of the function.
BTW I have to use the variables that is writing in the function (float m1[ROWS][COLS], float m2[ROWS][COLS], float ***C).
int mat_mul(float m1[ROWS][COLS], float m2[ROWS][COLS], float ***C)
{
int i, j, k;
C = (float ***)malloc(sizeof(float*) * 3);
for (i = 0; i < 3; i++) {
C[i] = (float **)malloc(sizeof(float*) * 3);
for (j = 0; j < 3; j++) {
C[i][j] = (float *)malloc(sizeof(float) *3);
}
}
for (i = 0; i < ROWS; i++) {
for (j = 0; j < COLS; j++) {
(*C)[i][j] = 0;
for (k = 0; k < ROWS; k++) {
(*C)[i][j] += m1[i][k] * m2[k][j];
}
}
}
printf_s("%.1f\n", (*C)[0][0]);
}
int i,j;
float Results[ROWS][COLS];
float Angle1[6], Angle2[6];
Angle_Reader("data_q.csv", &Angle1, &Angle2);
Angle_Converter(&Angle1, &Angle2);
for (i = 0; i < 1; i++) {
float Matrix1[ROWS][COLS] = { {cos(Angle1[i]),-sin(Angle1[i]),L1*cos(Angle1[i])},{sin(Angle1[i]),cos(Angle1[i]),L1*sin(Angle1[i])},{0,0,1} };
float Matrix2[ROWS][COLS] = { {cos(Angle2[i]),-sin(Angle2[i]),L2*cos(Angle2[i])},{sin(Angle2[i]),cos(Angle2[i]),L2*sin(Angle2[i])},{0,0,1} };
mat_mul(&Matrix1, &Matrix2, &Results);
}
printf_s("\n");
printf_s("%.1f\n", Results[0][0]);
This:
for (i = 0; i < 1; i++) {
float Matrix1[ROWS][COLS] = { {cos(Angle1[i]),-sin(Angle1[i]),L1*cos(Angle1[i])},{sin(Angle1[i]),cos(Angle1[i]),L1*sin(Angle1[i])},{0,0,1} };
float Matrix2[ROWS][COLS] = { {cos(Angle2[i]),-sin(Angle2[i]),L2*cos(Angle2[i])},{sin(Angle2[i]),cos(Angle2[i]),L2*sin(Angle2[i])},{0,0,1} };
mat_mul(&Matrix1, &Matrix2, &Results);
}
Should not be in a loop as is.
Either move the declaration above the loop, then in the loop, use index values ( i.e. Matrix2[i][j] ) instead of ROWS, COLS,
float Matrix1[ROWS][COLS] = {0};
float Matrix2[ROWS][COLS] = {0};
//Note: these initializers work only for 3x3 array
//Forcing ROWS == 3 and COLS == 3
float data1[ROWS][COLS] = { {cos(Angle1[0]),-sin(Angle1[1]),L1*cos(Angle1[2])},{sin(Angle1[0]),cos(Angle1[1]),L1*sin(Angle1[2])},{0,0,1} };
float data2[ROWS][COLS] = { {cos(Angle2[0]),-sin(Angle2[1]),L2*cos(Angle2[2])},{sin(Angle2[0]),cos(Angle2[1]),L2*sin(Angle2[2])},{0,0,1} };
...
if(Results)
{
for (i = 0; i < 1; i++)
{
Matrix1[i][j] = data1[i][j];
Matrix2[i][j] = data2[i][j];
...
mat_mul(&Matrix1, &Matrix2, &Results);
}
...or remove the for loop altogether and modify the i and j indexes from Angle1[i] to hard-coded values eg: Angle1[0], Angle1[1],... so that the initializers will populate the 2D arrays.
float Matrix1[ROWS][COLS] = { {cos(Angle1[0]),-sin(Angle1[1]),L1*cos(Angle1[2])},{sin(Angle1[0]),cos(Angle1[1]),L1*sin(Angle1[2])},{0,0,1} };
float Matrix2[ROWS][COLS] = { {cos(Angle2[0]),-sin(Angle2[1]),L2*cos(Angle2[2])},{sin(Angle2[0]),cos(Angle2[1]),L2*sin(Angle2[2])},{0,0,1} };
mat_mul(&Matrix1, &Matrix2, &Results);
Regarding creating memory, Nothing in your code indicates the need for a 3D array. As stated in comments, the reason for float ***C in the mat_mul(., ., float ***C) function prototype is to accommodate passing the address of a 2D matrix ( eg. &Results ), when calling function so that it can be modified. Even then, it would be an improvement to move the logic necessary to create the memory for that variable into its own function, and allocate the memory before it is passed as a variable:
float **Results = Create2D(COLS, ROWS);
if(Results)
{
for (i = 0; i < 1; i++)
{
...
...
mat_mul(&Matrix1, &Matrix2, &Results);
}
//When finished using, free Results
free2D(Results, COLS)
The functions Create2D() and its companion could be implemented as:
float ** Create2D(int c, int r)
{
float **arr;
int y;
arr = calloc(c, sizeof(float *));
for(y=0;y<c;y++)
{
arr[y] = calloc(r, sizeof(float));
}
return arr;
}
void free2D(float **arr, int c)
{
int i;
for(i=0;i<c;i++)
{
free(arr[i]);
}
free(arr);
}

Direct sum of matrix function

This struct allows representing arbitrary size matrices, where M is the number of rows, N is the number of columns and data is a pointer to M*N values of type double stored by rows.
struct matrix {
size_t M, N;
double *data;
};
struct matrix *mat_directsum(const struct matrix *a, const struct matrix *b);
The function mat_directsum accepts two pointers to arrays as a parameter and should return to the direct sum, dynamically allocated on the heap.
Example:
A.M = 2
A.N = 3
A.data = (1, 1, 2, 0, 1, -3)
Example of direct sum function
I just need a few tips on how to set the function, just to see how others work with arrays of this type, because the only way that comes to mind is an iterative methods with many loops, however, it is enough work long and ingenious, I would like to know if there are easier method to solve it. Thank you
ps.
(memory allocation is not a problem of course)
EDIT
I solved like that:
struct matrix *mat_directsum(const struct matrix *a, const struct matrix *b) {
struct matrix *c = malloc(sizeof(struct matrix));
c->M = a->M + b->M;
c->N = a->N + b->N;
int n = c->M * c->M;
double *dati = calloc(n, sizeof(double));
int t = 0;//index new array
int y = 0;//index first mat
int z = 0;//index second mat
for (int i = 0; i < c->N; i++) {
if (i < a->N) {//first mat
for (int j = 0; j < c->M; j++) {
if (j < a->M) {
dati[t] = a->data[y];
y++;
}
t++;
}
} else {//second mat
for (int j = 0; j < c->M; j++) {
if (j >= a->M) {
dati[t] = b->data[z];
z++;
}
t++;
}
}
}
c->data = dati;
return c;
}
I don't know how to do it which only one for loop
//macro which will point to an element indexed at [xe][ye]
#define ELEMENT(data,rows,columns,xe,ye) (data+((xe)*(columns)+(ye)))
struct matrix
{
size_t M, N;
double *data;
};
//won't mind changing the return type from "struct matrix*" to "struct matrix"
struct matrix mat_directsum(const struct matrix *a, const struct matrix *b)
{
int x;
struct matrix res;
res.M = a->M + b->M;
res.N = a->N + b->N;
//using calloc will set the memory to zero i.e all the bytes will be set to zero.
res.data = (double*)calloc(res.M * res.N, sizeof(double));
if(res.data == NULL)
{
return res;
}
for(x = 0; x < a->M; ++x)
{
memcpy(ELEMENT(res.data, res.M, res.N, x, 0), ELEMENT(a->data, a->M, a->N, x, 0), a->N * sizeof(double));
}
for(x = 0; x < b->M; ++x)
{
//note the offset by [a->M][a->N] while accessing elements of res.
memcpy(ELEMENT(res.data, res.M, res.N, x + a->M, a->N), ELEMENT(b->data, b->M, b->N, x, 0), b->N * sizeof(double));
}
return res;
}
struct matrix res = mat_directsum(&a, &b);
if(res.data != NULL)
{
free(res.data);
}
Besides the error n = c->M * c->M, spotted by M.M (what a coincidence of Ms!), your solution has another error in the for loops: You confused the row and column numbers M and N - since the values are stored by rows, the outer loop has to be for (int i = 0; i < c->M; i++) and the inner loops have to be for (int j = 0; j < c->N; j++), so all M and N in those loops (also in the ifs) have to be swapped. Apart from that and the missing allocation error checks, your solution is fine.
I don't know how to do it which only one for loop
If you want to see an alternative approach, here's one with a helper function to insert the matrices into the sum matrix:
#include <string.h>
void mat_insert(const struct matrix *s, struct matrix *d, int r, int c)
{ // copy source matrix s to destination matrix d at row r, column c
for (int i = 0; i < s->M; i++) // for each row
memcpy(d->data+(r+i)*d->N+c, s->data+i*s->N, s->N*sizeof*s->data);
}
struct matrix *mat_directsum(const struct matrix *a, const struct matrix *b)
{
struct matrix *c = malloc(sizeof *c); if (!c) return NULL;
c->M = a->M + b->M;
c->N = a->N + b->N;
int n = c->M * c->N;
c->data = calloc(n, sizeof *c->data); if (!c->data) return free(c), NULL;
mat_insert(a, c, 0, 0); // copy a to c at row 0, column 0
mat_insert(b, c, a->M, a->N); // copy b to c at row a->M, column a->N
return c;
}

Resources