⚠️ 해당 포스팅은 인프런 공룡책 강의를 듣고 개인적으로 정리하는 글입니다. 정확하지 않은 정보가 있을 수 있으니 주의를 요합니다.
Chapter 4 Threads
하나의 분주한 웹 서버는 수천, 수만 개의 클라이언트들이 병행하게 접근할 수 있다.만약 웹 서버가 전통적인 단일 스레드 프로세스로 작동한다면, 자신의 단일 프로세스로 한 번에 하나의 클라이언트만 서비스할 수 있게 되어 클라이언트자신의 요구가 서비스되기까지 매우 긴 시간을 기다려야 한다. 이러한 단점을 개선하기 위해서 여러 요청을 수행할 별도의 프로세스들을 만들 수도 있지만 이는 많은 리소스가 필요함으로 많은 오버헤드가 발생한다. 프로세스를 새로 만드는 것 보다 프로세스의 개념을 확장하여 한 프로세스가 다수의 실행 스레드를 가질 수 있도록 허용한다. 그들은 프로세스가 한 번에 하나 이상의 일을 수행할 수 있도록 허용함으로써 문제를 해결한다. 이를 웹 서버에 적용 시켜보면, 웹 서버가 다중 스레드화 되게끔 만들어서 서버는 클라이언트의 요청을 listen 하는 별도의 스레드를 생성한다. 요청이 들어오면 다른 프로세스를 생성하는 것이 아니라, 요청을 서비스할 새로운 스레드를 생성하고 추가적인 요청을 listen 하기 위한 작업을 재개한다. (번역 출처)
병렬 실행 시 두 가지 유형이 있다.
: 데이터 병렬 : 데이터를 쪼개 각각의 코어에 분산하고, 동일한 연산을 수행한다.
: 태스크 병렬 : 데이터가 아닌 태스크를 쪼개 각각의 코어에 분산한다.
: 분산 시스템의 개발로 크게 중요한 부분은 아니다.
Amdahl's Law, 코어는 무조건 많을수록 좋은가?
: 예를 들어 코어가 2개일 때 처리 속도가 2라고 가정했을 때 코어가 4개이면 처리 속도는 4가 되는걸까?
: 정답은 그렇지 않다.
: 모든 처리가 병렬로 이뤄지면 그런 결과가 나올 수도 있겠지만, 순차적으로 처리해야만 하는 작업이 있기 때문에 기대만큼의 성능 향상이 이뤄지진 않는다.
(그러나 일반적으로는 구현까지도 함께 의미한다.)
/* the data shared by the threads */
int sum;
/*thread call this functions */
void * runner(void *param);
int main(int argc, char *argv[])
{
pthread_t tid; // Thread id
pthread_attr_t attr; // Thread attributes
pthread_attr_init(*attr);
pthread_create(&tid, &attr, runner, argv[1]); // argv[1] -> runner paramter
pthread_join(tid, NULL); // 스레드 종료까지 대기
printf("sum = %d\n", sum);
}
void *runner(void *param)
{
int i, upper = atoi(param);
sum = 0;
for (i = 0; i <= upper; i++)
sum += i;
pthread_exit(0);
}
정답은 아래 코드의 주석에 기재
pid_t pid;
pid = fork();
if (pid == 0) { // child process
fork();
thread_create( . . . );
}
fork();
/* 프로세스 총 6개, 스레드는 총 2개 생성 */
(아래에 실습 코드 기재)
/* OpenMP Test Code */
#include <omp.h>
int main(int argc, char *argv[])
{
#pragma omp parallel // compiler directive (시스템 코어의 개수만큼 스레드를 생성)
{
printf("I am a parallel region.\n"); // 코어가 4개면 4줄 프린트
}
return 0;
}
---
#include <omp.h>
int main(int argc, char *argv[])
{
omp_set_num_threads(4); // 생성 스레드 개수를 지정할 수도 있음
#pragma omp parallel // compiler directive (지정 개수만큼 스레드를 생성)
{
printf("I am a parallel region.\n"); // 4줄 프린트
}
return 0;
}
#include <omp.h>
#define SIZE 10000000
int main(int argc, char *argv[])
{
int i;
for (i = 0; i < SIZE; i++)
a[i] = b[i] = i;
#pragma omp parallel for // 병렬 처리
for (i = 0; i < SIZE; i++) {
c[i] = a[i] + b[i];
}
/*
처리 결과 시간 측정
병렬 처리 없이 돌렸을 때
real 0m0.586s -> 유저 스레드, 커널 스레드 처리 시간을 더함
user 0m0.364s
sys 0m0.223s
병렬 처리와 함께 돌렸을 때
real 0m0.423s -> 커널 스레드의 시간만 (더 빠름)
user 0m1.091s -> 관여하지 않고 병렬 처리동안 대기
sys 0m0.441s -> 커널 스레드가 모두 처리
*/
}