공유메모리를 사용한 병렬처리 가우스소거법 프로그램

공유메모리를 사용한 병렬처리 가우스소거법 프로그램

작성일 2024.04.09댓글 1건
    게시물 수정 , 삭제는 로그인 필요

안녕하세요 공유메모리를 이용한 병렬처리로 가우스소거법을 구현하고 있습니다.
아래는 코드입니다.

[code]
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <pthread.h>
#include "wrapper.h"
#define sfname "sha"

double * A, * B, * X;
pthread_barrier_t *barrier;


void argCheck(int argc, char *argv[])
{
  /*
    Fill the function with codes such that
    if the number of arguments is not 4
       exit with code 1 and the following error message
           Usage: hw1 <mat> <invec> <outvec>
   */
    if (argc != 5)
    {
        fprintf(stderr, "Usage: %s <mat> <invec> <outvec> <np>\n", argv[0]);
        exit(1);
    }
}

int sizeCheck(int argc, char *argv[])
{
  /*
     Fill the function with codes such that 
     if the number of elements in file argv[1] is the square of the argv[2]
        return the number of elements in file argv[2]
     else
        exit with value 1
  */
    struct stat finfoA, finfoB;
    int noeleA, noeleB;

    Stat(argv[1], &finfoA);
    Stat(argv[2], &finfoB);
    noeleA = finfoA.st_size / sizeof(double);
    noeleB = finfoB.st_size / sizeof(double);
    if (noeleA != noeleB * noeleB)
    {
        fprintf(stderr, "Error: no. elements(%d) of %s is not square of that(%d) of %s.\n", noeleA, argv[1], noeleB, argv[2]);
        exit(1);
    }
    return noeleB;
}

void getData(int size, int argc, char *argv[])
{
    int afd, bfd;

    A = (double*)malloc(sizeof(double) * size * size);
    B = (double*)malloc(sizeof(double) * size); //어떤 사이즈에도 대응하기 위해 동적할당
    X = (double*)malloc(sizeof(double) * size); //병렬처리를 수행하게되면 필요없을 수도 있음

    afd = Open(argv[1], O_RDONLY);
    bfd = Open(argv[2], O_RDONLY);
    Read(afd, A, sizeof(double) * size * size);
    Read(bfd, B, sizeof(double) * size);
    close(afd);
    close(bfd);
}

void GaussianElimination(int n, int start, int end)
{
    int l, i, j;

    for (l = 0; l < n - 1; l++) {
        for (i = start; i < end; i++)
        {
            double ratio = A[i * n + l] / A[l * n + l];
            for (j = l; j < n; j++)
            {
                A[i * n + j] = A[i * n + j] - ratio * A[l * n + j];
                //A[i * n + j] = A[i * n + j] - (A[i * n + l] / A[l * n + l]) * A[l * n + j]; //1차원 배열로 표현
            }//n : 한 행의 요소 수, i : 행 번호, j : 열 번호
            B[i] = B[i] - ratio * B[l];
            //B[i] = B[i] - (A[i * n + l] / A[l * n + l]) * B[l];
        }
        pthread_barrier_wait(barrier);
    }
}

void BackSubstitution(int n, int start, int end)
{
    int i, j;

    for (i = n - 1; i >= 0; i--)
    {
        if (i >= start && i < end)
        {
            X[i] = B[i] / A[i * n + i];
        }
        pthread_barrier_wait(barrier);

        for (j = start; j < end && j < i; j++)
        {
            B[j] -= X[i] * A[j * n + i];
            A[j * n + i] = 0;
        }
        pthread_barrier_wait(barrier);
    }
}

int main(int argc, char *argv[])
{
    pthread_barrierattr_t attr; //특성변수

    int xfd;
    int size;
    int np;

    pid_t pid;

    argCheck(argc, argv);
    size = sizeCheck(argc, argv);
    np = atoi(argv[4]);
    getData(size, argc, argv);

    int smsize = (size * size + size + size + sizeof(pthread_barrier_t)) * sizeof(double);
    int smfd = Shm_open(sfname, O_CREAT | O_RDWR, 0666);
    Ftruncate(smfd, smsize);
    double* shm_ptr = (double*)Mmap(0, smsize, PROT_READ | PROT_WRITE, MAP_SHARED, smfd, 0);

    A = shm_ptr;
    B = shm_ptr + size * size;
    X = shm_ptr + size * size + size;
    barrier = (pthread_barrier_t*)(X + size);

    Pthread_barrierattr_init(&attr);
    Pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    Pthread_barrier_init(barrier, &attr, np);

    for (int i = 0; i < np; i++)
    {
        pid = fork();
        if (pid == 0) //자식 프로세스
        {
            int start = i * (size / np);
            int end = ((i + 1) * size) / np - 1;
            if (i == np - 1)
            {
                end = size;
            }
            GaussianElimination(size, start, end);
            pthread_barrier_wait(barrier);
            BackSubstitution(size, start, end);
            exit(0);
        }
    }

    for (int i = 0; i < np; i++)
    {
        wait(NULL);
    }

    xfd = Creat(argv[3], 0644);
    Write(xfd, X, sizeof(double) * size);
    close(xfd);

    pthread_barrier_destroy(barrier);
    munmap(shm_ptr, smsize);
    shm_unlink(sfname);    

    //free(A);
    //free(B);
    //free(X);

    return 0;
}
[/code]

대문자로 시작하는 함수들은 wrapper함수로 구현해놓은 것들입니다.
여튼 이렇게 해서 컴파일도 성공적으로 되고 
./'프로그램이름' A2.dat B2.dat X2.dat '프로세스 수'
이런식으로 실행합니다. A는 행렬 B는 벡터입니다. 그래서 AX = B를 계산해서 X에 벡터가 구해집니다.

그리고 ./showdata X2.dat를 해서 내용을 확인할 수 있는데 -nan -nan이라고 나옵니다... 숫자가 아니라는 의미인 것은 알고 있는데 딱히 0으로 나눈다거나 하는 경우가 안생기는 것 같은데 어디가 문제인지 모르겠습니다 ㅜ start와 end에서 반복문을 나누는 곳이 잘못된걸까요? 답변부탁드립니다.



profile_image 익명 작성일 -

질문자님의 공유 메모리를 사용한 병렬처리 가우스 소거법 프로그램 코드를 살펴보았습니다. 문제 해결을 위해 몇 가지 포인트를 짚어보겠습니다.

1. **BackSubstitution 함수의 범위 계산**:

- `BackSubstitution` 함수에서 `i >= start && i < end` 조건으로 X[i]를 계산하는 부분이 있습니다. 이 조건은 각 프로세스가 자신의 범위 내에서만 X[i] 값을 계산하도록 합니다. 그러나 역대입 과정은 모든 행에 대해 수행되어야 하는데, 이 로직은 마지막 프로세스가 마지막 행의 X[i]만 계산하게 할 수 있습니다. 따라서, 역대입 과정에서 계산되어야 할 X[i]가 올바르게 계산되지 않을 가능성이 있습니다.

2. **프로세스 분할 범위 계산**:

- `main` 함수에서 각 프로세스의 작업 범위를 계산할 때 `end`의 계산이 `((i + 1) * size) / np - 1`으로 되어 있습니다. 이는 각 프로세스가 처리할 마지막 행을 한 줄 덜 처리하게 만들 수 있습니다. 마지막 프로세스에 대해서만 `end = size;`로 조정하는 것이 아니라, 모든 프로세스에 대해 올바르게 범위를 설정해주어야 합니다. 특히, 가우스 소거법에서는 모든 행이 올바르게 처리되어야 정확한 결과를 얻을 수 있습니다.

3. **-nan 값의 출현 원인**:

- `-nan` 값은 보통 'Not a Number' 상황, 즉 0으로 나누기 연산과 같은 수학적으로 유효하지 않은 연산을 수행했을 때 발생합니다. 이는 `GaussianElimination` 함수에서 분모가 0이 되는 상황, 즉 `A[l * n + l]` 값이 0이 될 때 발생할 수 있습니다. 이를 방지하기 위해서는 해당 값이 0에 가까운지 먼저 확인하는 로직을 추가하는 것이 좋습니다.

4. **동적 메모리 할당 및 해제**:

- 프로그램 시작 부분에서 `A`, `B`, `X`에 대한 메모리를 동적 할당하고 있지만, 실제로는 공유 메모리를 이용하기 때문에 이 부분의 동적 할당은 필요 없습니다. `//free(A); //free(B); //free(X);` 부분이 주석 처리되어 있으나, 실제로는 이 부분의 할당 로직 자체가 불필요합니다.

5. **시작 및 끝 인덱스 조정**:

- `GaussianElimination` 및 `BackSubstitution` 함수에서 시작(`start`) 및 끝(`end`) 인덱스를 사용하여 반복문을 제어하고 있습니다. `end`의 계산 방식을 `int end = (i + 1) * (size / np);`로 조정하고, 마지막 프로세스의 경우 `end = size;`를 유지하면, 각 프로세스가 처리해야 할 범위를 보다 정확하게 나눌 수 있습니다.

해당 포인트들을 바탕으로 코드를 재검토하고 수정하시면 `-nan` 문제를 해결하실 수 있을 것으로 보입니다. 만약 수정 후에도 문제가 지속된다면, `GaussianElimination`과 `BackSubstitution` 함수에서 실제 데이터의 변화를 디버깅하여 정확확인하시는 것이 좋겠습니다. 구체적으로, 디버깅을 위한 로그 출력이나 조건 검사를 추가하여, 각 단계에서의 `A`, `B`, `X` 배열의 값이 올바르게 계산되고 있는지 확인해 보세요.

또한, `-nan` 값이 발생하는 구체적인 원인을 파악하기 위해서는 다음과 같은 접근 방법이 유용할 수 있습니다:

1. **분모가 0인 경우 확인**: `GaussianElimination` 함수에서 `A[l * n + l]`이 0인 경우를 대비해 로그를 출력하거나, 해당 경우에 대한 예외 처리를 해주는 코드를 추가하세요. 이는 0으로 나누는 연산을 방지합니다.

2. **데이터 범위 검증**: 입력 데이터(`A`, `B` 배열)의 값들이 예상 범위 내에 있는지 확인해보세요. 이상치나 너무 크거나 작은 값이 없는지 검사하는 것이 중요합니다.

3. **반복문 범위 오류 수정**: `GaussianElimination`과 `BackSubstitution` 함수에서 사용되는 `start`와 `end`의 범위가 모든 필요한 행과 열을 커버하고 있는지 다시 한 번 확인하세요. 특히, `end`를 설정할 때 마지막 프로세스가 배열의 마지막 요소까지 올바르게 처리하고 있는지 검증이 필요합니다.

4. **디버깅을 위한 로그 추가**: 코드의 핵심 부분에 `printf`를 사용하여 중간 계산 결과를 출력하고, 그 결과가 예상과 일치하는지 확인합니다. 예를 들어, `GaussianElimination`과 `BackSubstitution` 함수의 각 단계에서 `A`, `B`, `X` 배열의 값들을 출력해볼 수 있습니다.

5. **동기화 문제 검토**: `pthread_barrier_wait(barrier);`를 사용한 동기화가 올바르게 작동하고 있는지 확인합니다. 모든 프로세스 또는 스레드가 예상대로 동기화 포인트에서 기다리고 있는지, 누락되거나 잘못된 동기화로 인해 일부 계산이 제대로 수행되지 않는 경우가 없는지 점검하세요.

위 사항들을 체크하시며 코드를 수정하고, 여전히 문제가 해결되지 않는다면, 구체적인 입력 데이터 예시나 오류가 발생하는 상황에 대한 추가 정보를 제공해 주시면 더 정확한 도움을 드릴 수 있습니다. 질문자님의 프로젝트가 성공적으로 완성되기를 바랍니다.

병렬처리

... 레벨의 사용자 인터페이스 제공 병렬 처리 시스템의... 제어 - 공유 메모리를 차지하기 위한 프로세스간... 담당하고 사용프로그램만 수행 - 구현이 쉬우나...

메모리 높을수록 좋은가?

... 3기가 메모리1기가 지포스 6600(256) 입니다 사용프로그램은... 병렬처리가 가능하도록 만들 어야 효과를 볼 수 있습니다. 그러므로 일반적인 사용자가 사용하는 프로그램은...

병렬은 무슨 원리로 따로따로 꺼지나요 ?

... 기상처리나 시뮬레이션등에 사용됩니다. 또는 컴퓨터를 네트워크를 이용한 병렬로 여러개로 붙여서 작업을 각... 동작하고, 메모리의 영역도 프로그램마다 독립적으로...

병령처리 밥법의 예를 좀 설명해주세요..

도와주세요~ 병렬처리에 대한 소개 컴퓨터에서 병렬처리프로그램 명령어를 여러... 공유 메모리를 사용하는 대신 프로그램간 메시지 전송을 위해 네트웍를 이용하는...

vpn지우는법

... 3."네트워크 및 공유 센터"를 클릭합니다. 4."안전한 VPN... 검토하고 사용하지 않는 프로그램을 제거합니다. 4.하드웨어 구성을 확인합니다. 충분한 메모리, 공간 및 처리...

아이폰 + 맥북 vs 갤럭시 + 갤럭시북

... 인터넷은 메모리를 많이 사용하기 때문에 16GB 이상으로 구매하는 것을 추천합니다. 저장장치 고르는 ... 개발자라면 M2 칩이 병렬처리 행렬 연산 코드 수행에...

맥북m2, 맥북 프로 16 pro m1 에는...

... 인터넷은 메모리를 많이 사용하기 때문에 16GB 이상으로 구매하는 것을 추천합니다. 저장장치 고르는 ... 개발자라면 M2 칩이 병렬처리 행렬 연산 코드 수행에...