본문 바로가기

대학원 수업관련

Thread를 이용한 Matrix Multiply

출처 : http://inhack.org/wordpress/?p=3390


POSIX 멀티 쓰레딩을 이용한 행렬 곱셈 프로그램입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
 
#define MaxColumns 65536
 
// Matrix(행렬)의 정보가 들어가는 구조체
typedef struct MatrixInfo{
    int rows;       // 행
    int columns;    // 렬
}MatInfo;
 
// Matrix Multiplication을 수행할 쓰레드에 넘겨줄 구조체 인자
typedef struct Thread_Parameter{
    int **A,**B,**Result;               // 입력 행렬 두개와 결과값이 담길 행렬
    MatInfo A_info,B_info,Result_info;  // 위 세 행렬의 정보가 담긴 구조체
    int x;
}param;
 
// 입력 행렬을 파일로부터 읽어들이는 함수
char* ReadFile2Str(char* filename, MatInfo *inform);
// 행렬의 열, 컬럼 개수를 얻어오는 함수
int GetColumns(char* str);
// 표줜출력으로 행렬 내용을 출력해주는 함수
void PrintMatrix(int **mat, MatInfo inform, const char* str);
// 행렬의 값을 Result.txt 라는 파일에 Write하는 함수
void WriteMatrix2File(int **mat, MatInfo inform, FILE* fp);
// 실행할 쓰레드
void *thread_func(void* arg);
// 쓰레드 내에서 실제적으로 행렬 곱을 계산하는 함수
void *Calc_Matrix(int **A, int **B, int **Result, MatInfo A_info, MatInfo Result_info,int x);
 
int main(int argc, char** argv){
    MatInfo A_info,B_info,Result_info;
    int **A,**B,**Result;
    int i,j;
    char *Astr=NULL,*Bstr=NULL;
    char* token;
 
    pthread_t *p_thread;
    void *thr_result;
 
    param t_param;
    param *p_t_param=NULL;
 
    FILE* wfp = fopen("./Result.txt","w");      // 결과값을 저장할 파일의 핸들 open
 
    if(argc!=3){    // 커맨드라인인자로 피연산 행렬 두개가 담긴 파일을 입력받는다
        printf("[*] Usage : %s [InputFile1] [InputFile2]\n",argv[0]);
        printf("[*] InputFile Format\nex > 4*3 Matrix\n1 2 1\n3 4 2\n5 7 6\n9 2 3\n");
        exit(EXIT_FAILURE);
    }
 
    // 각 파일로부터 행렬의 값을 한줄로 만들어 읽어옴 (나중에 토큰으로 분리해 행렬 만듬)
    Astr = ReadFile2Str(argv[1],&A_info);
    Bstr = ReadFile2Str(argv[2],&B_info);
 
    // 첫번째 행렬의 열과 두번째 행렬의 행이 다를 경우 곱셈을 진행할 수 없음
    if(A_info.columns!=B_info.rows){
                printf("A Columns and B Rows must be equal!\n");
                exit(EXIT_FAILURE);
        }
 
    // 결과값이 담긴 행렬의 행과 열을 세팅
    Result_info.rows = A_info.rows;
    Result_info.columns = B_info.columns;
 
    // dynamic memory assignment
    // 읽어들인 행렬값을 2차원 정수배열에 담기 위해 동적 메모리 확보 (크기만큼)
    A = (int**)malloc(sizeof(int*)*A_info.rows);
    for(i=0;i<A_info.rows;i++){
            A[i] = (int*)malloc(sizeof(int)*A_info.columns);
    }
    B = (int**)malloc(sizeof(int*)*B_info.rows);
    for(i=0;i<B_info.rows;i++){
            B[i] = (int*)malloc(sizeof(int)*B_info.columns);
    }
    Result = (int**)malloc(sizeof(int*)*Result_info.rows);
    for(i=0;i<Result_info.rows;i++){
            Result[i] = (int*)malloc(sizeof(int)*Result_info.columns);
    }
 
    // 파일로부터 읽어들인 내용을 한줄 행렬을 " " 공백을 기준으로 위에서 확보한 정수형 2차원 배열에 값을 할당,대입
    token=NULL;
    token = strtok(Astr," ");
    for(i=0;i<A_info.rows;i++){
        for(j=0;j<A_info.columns;j++){
            A[i][j]=atoi(token);
                    token = strtok(NULL," ");
            if(token==NULL) break;
        }
    }
 
    token=NULL;
    token = strtok(Bstr," ");
    for(i=0;i<B_info.rows;i++){
        for(j=0;j<B_info.columns;j++){
            B[i][j]=atoi(token);
                    token = strtok(NULL," ");
            if(token==NULL) break;
        }
    }
 
    // 표준출력으로 입력받은 행렬 두개 출력
    PrintMatrix(A,A_info,"======== A ========\n");
    PrintMatrix(B,B_info,"======== B ========\n");
 
    // 다중 쓰레딩을 위해 각 쓰레드들을 참조할 때 사용할 객체들을 A 행렬의 행만큼 동적메모리 확보 (나중에 쓰레드를 생성하는 pthread_create의 첫번째 인자)
    p_thread = (pthread_t *)malloc(sizeof(pthread_t)*A_info.rows);
    // 각 쓰레드들에 넘겨줄 구조체를 다중 쓰레드의 개수만큼 동적으로 메모리로 확보
    p_t_param = (param*)malloc(sizeof(param)*A_info.rows);
 
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////// 실제적으로 다중 쓰레드를 생성하는 부분 //////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    for(i=0;i<Result_info.rows;i++){
        int flag;
        p_t_param[i].A=A;
        p_t_param[i].B=B;
        p_t_param[i].Result=Result;
        p_t_param[i].A_info=A_info;
        p_t_param[i].B_info=B_info;
        p_t_param[i].Result_info=Result_info;
        p_t_param[i].x=i;
        // 각 쓰레드들에 다중인자(구조체)를 넘기면서 구조체 생성
        flag = pthread_create(&(p_thread[i]),NULL,thread_func,(void *)(p_t_param+i));
        if(flag!=0){
            perror("Thread Creation Failed!\n");
            exit(EXIT_FAILURE);
        }
    }
 
    // 생성된 쓰레드가 일을 마치고 종료될때까지 기다리는 함수
    for(i=0;i<Result_info.rows;i++){
        int flag;
        flag = pthread_join(p_thread[i],&thr_result);
        if(flag!=0){
            perror("Thread Join Failed!\n");
        }
    }
 
    // 계산된 결과 행렬 값을 표준출력
    PrintMatrix(Result,Result_info,"========= Result ========\n");
    // 계산된 결과 행렬 값을 파일에 출력 (Result.txt)
    WriteMatrix2File(Result,Result_info,wfp);
 
    // 확보한 동적메모리들을 해제하는 부분
    for(i=0;i<A_info.rows;i++){
                free(A[i]);
        }
        free(A);
        for(i=0;i<B_info.rows;i++){
                free(B[i]);
        }
        free(B);
 
        for(i=0;i<Result_info.rows;i++){
                free(Result[i]);
        }
        free(Result);
    free(p_thread);
    free(p_t_param);
    fclose(wfp);
 
    exit(EXIT_SUCCESS);
 
}
 
char* ReadFile2Str(char* filename, MatInfo* inform){
    FILE* fp;
    char* strMat;
    int i,j;
    struct stat fileinfo;
    char* column;
 
    fp = fopen(filename,"r");
    lstat(filename,&fileinfo);
 
    inform->rows=0;
 
    strMat = (char *)malloc(sizeof(char)*fileinfo.st_size);
 
    // 여러줄로 되어있는 행렬을 한줄로 만들면서, 행렬의 행값과 열값을 구함
    while(1){
        char buf[MaxColumns] = {0,};
        int length;
        fgets(buf,MaxColumns,fp);
        length = strlen(buf);
        buf[length-1]=' ';
        strcat(strMat,buf);
        if(length==0)   break;
        else    inform->columns=GetColumns(buf);
        inform->rows++;
    }
 
    return strMat;
}
 
int GetColumns(char* str){
    char* token;
    int columns=0;
    token = strtok(str," ");
    while(token!=NULL){
        columns++;
        token = strtok(NULL," ");
    }
    return columns;
}
 
void PrintMatrix(int **mat, MatInfo inform, const char* str){
    int i,j;
    printf("%s",str);
    for(i=0;i<inform.rows;i++){
        for(j=0;j<inform.columns;j++){
            printf("%d  ",mat[i][j]);
        }
        printf("\n");
    }
}
 
void WriteMatrix2File(int **mat, MatInfo inform, FILE* fp){
    int i,j;
 
    for(i=0;i<inform.rows;i++){
        for(j=0;j<inform.columns;j++){
            fprintf(fp,"%d  ",mat[i][j]);
        }
        fprintf(fp,"\n");
    }
 
}
 
void *thread_func(void* arg){
    param argp = *(param*)arg;
 
    Calc_Matrix(argp.A,argp.B,argp.Result,argp.A_info,argp.Result_info,argp.x);
 
    pthread_exit(NULL);
}
 
void *Calc_Matrix(int **A, int **B, int **Result, MatInfo A_info, MatInfo Result_info,int x){
    int tmp=0;
    int i,j;
        for(i=0;i<Result_info.columns;i++){
            for(j=0;j<A_info.columns;j++){
                        tmp += A[x][j]*B[j][i];
                }
                Result[x][i]=tmp;
 
        }
}

 - 주석을 달아놓았으니, 참고하시면 될것 같습니다.

 - 쓰레딩을 돌릴 때 gcc 옵션에 -lpthread 라는 옵션을 넣어주셔야 컴파일이 됩니다.

 - 프로그램 자체가 파일 두개에서 행렬을 입력받아 계산해서, Result.txt라는 파일에 결과값을 write합니다.

 - 입력에 사용할 두 행렬은 각각 inputA.txt(8×10)와 inputB.txt(10×7)에 저장되어 있습니다.

 - 실행이 잘된 것을 확인할 수 있습니다.

'대학원 수업관련' 카테고리의 다른 글

DAS/NAS/SAN/iSCSI 장단점  (0) 2014.06.18
#1 CUDA설치 하기(3.2버전, VS2008)  (1) 2014.05.20
Thread 함수 참고  (0) 2014.03.31
CreateThread, _beginthread, _beginthreadex의 차이  (0) 2014.03.29
Thread 발표 참고 링크  (0) 2014.03.28