본문 바로가기
SP

[sp] Thread-Level Parallelism

by 녕인뉸 2022. 5. 17.

1. Exploiting parallel execution

(1) I/O delay를 다루기 위해 쓰레드를 사용

ex) 클라이언트 당 하나의 쓰레드는 다른 쓰레드를 지연시키는 것을 막기 위해 사용

 

(2) Multi-core (4) /Hyperthreaded (8) CPU가 제공하는 것

 - 쓰레드를 병렬로 수행시키면서 작업을 분산

 - 많은 독립적인 task가 있을 경우 자동적으로 발생

ex) 많은 어플리케이션을 실행하거나 많은 클라이언트를 서빙할 경우

- 하나의 큰 테스크를 빠르게 만들기 위한 코드를 작성

ex) 여러 개의 병렬 sub-task로 구성됨

 

2. Typical Multicore Processor

단일 칩 안에 여러 개의 core (cpu)가 들어있음

L1, L2 - private cache

L3 - shared cache (여러 cpu들이 동시에 접근)

 

 

3. Benchmark Machine

(1) /proc/cpuinfo로 부터 데이터를 얻음

(2) shark machines

§ Intel Xeon E5520 @ 2.27 GHz

§ Nehalem, ca. 2010

§ 8 Cores

§ Each can do 2x hyperthreading

 

4. ex1) Parallel Summation ; 0부터 n-1까지의 합 -> ((n-1)*n)/2

각 파티션마다 쓰레드들이 assign됨

 

(1) 1에서 n-1의 숫자들을 partition으로 쪼개서 t개의 partition을 만듦

(2) 각 파티션의 [n/t] 을 구함

(3) 각 t 쓰레드들은 하나의 range를 수행

(4) 단순함을 위해 n이 t의 배수라고 가정

 

(5) sol) psum-mutex ; 쓰레드들은 semaphore mutex에 의해 보호되는 글로벌 변수에 sum을 넣음

 

void *sum_mutex(void *vargp); /* Thread routine */

/* Global shared variables */
long gsum = 0; /* Global sum */
long nelems_per_thread; /* Number of elements to sum */
sem_t mutex; /* Mutex to protect global sum */

int main(int argc, char **argv)
{
    long i, nelems, log_nelems, nthreads, myid[MAXTHREADS];
    pthread_t tid[MAXTHREADS];
    
    /* Get input arguments */
    nthreads = atoi(argv[1]);
    log_nelems = atoi(argv[2]);
    nelems = (1L << log_nelems);
    nelems_per_thread = nelems / nthreads;
    sem_init(&mutex, 0, 1);
    
    /* Create peer threads and wait for them to finish */
    for (i = 0; i < nthreads; i++) {
        myid[i] = i;
        Pthread_create(&tid[i], NULL, sum_mutex, &myid[i]);
    }
    for (i = 0; i < nthreads; i++)
    	Pthread_join(tid[i], NULL);
        
    /* Check final answer */
    if (gsum != (nelems * (nelems-1))/2)
    	printf("Error: result=%ld\n", gsum);
        
    exit(0);
}
/* Thread routine for psum-mutex.c */ //각 쓰레드가 실행하는 방법
void *sum_mutex(void *vargp)
{
    long myid = *((long *)vargp); /* Extract thread ID */
    long start = myid * nelems_per_thread; /* Start element index */
    long end = start + nelems_per_thread; /* End element index */
    long i;
    
    for (i = start; i < end; i++) {
        P(&mutex);
        gsum += i;
        V(&mutex);
    }
    return NULL;
}

 

각 쓰레드들은 gsum을 공유하면서 update시킴

 

- psum-mutex Performance

8 core를 가진 shark machine, n = 2^31

- 싱글 쓰레드는 매우 느림

- 많은 core를 사용할 수록 느려짐

 

 

(6) sol) psum-array 

- peer thread i는 글로벌 array의 element인 psum[i]에 합을 넣어줌

- main은 쓰레드들이 완료하기를 기다리고, 그 후 psum의 element들을 모두 더해줌

- mutex 동기화를 할 필요 없음

 

/* Thread routine for psum-array.c */
void *sum_array(void *vargp)
{
    long myid = *((long *)vargp); /* Extract thread ID */
    long start = myid * nelems_per_thread; /* Start element index */
    long end = start + nelems_per_thread; /* End element index */
    long i;
    
    for (i = start; i < end; i++) {
    	psum[myid] += i;
    }
    
    return NULL;
}

 

- psum-array Performance

 

psum-mutex보다 더 빠름

 

(7) sol) psum-local

 - 로컬 변수에 각 쓰레드들의 합을 넣어줌으로써 메모리 참조를 감소함

 - 레지스터에서 중간값을 update

 

/* Thread routine for psum-local.c */
void *sum_local(void *vargp)
{
    long myid = *((long *)vargp); /* Extract thread ID */
    long start = myid * nelems_per_thread; /* Start element index */
    long end = start + nelems_per_thread; /* End element index */
    long i, sum = 0;
    
    for (i = start; i < end; i++) {
    	sum += i; // reg에 있음
    }
    psum[myid] = sum;
    return NULL;
}

 

- psum-local performance

psum-array보다 더 빨라짐

 

 

5. Characterizing Parallel program performance

(1) p 프로세서 코어, Tk는 k 코어를 사용한 수행 시간

(2) speed up: Sp = T1/Tp  ; p개의 cpu를 사용하여 성능이 얼마나 빨라지는지

 - Sp : relative speedup ; T1이 하나의 코어에서 실행되는 코드의 병렬 버전의 수행 시간일 경우

 - Sp : absolute speedup ; T1이 하나의 코어에서 실행되는 코드의 sequential한 버전의 수행시간일 경우

        : parallelism의 이점에 대한 측정을 훨씬 더 정확하게 측정

 

(3) Efficiency : Ep = Sp/p = T1/(p*Tp)

-> (0, 100]

-> 병렬화로 인한 오버헤드 측정

 

ex) 

T1 = 1, T4 = 0.25 -> S4 = 1/0.25 = 4

-> E4 = 4/4 = 1  ---> 100

 

T4 = 0.5 -> S4 = 1/0.5 = 2

-> E4 = 2/4 = 1/2 ---> 50

 

 

6. Performance of psum-local

- 성능은 더 좋아졌지만 효율은 더 떨어짐

- 예시는 쉽게 병렬화 가능

- 실제 코드는 병렬화하기 훨씬 어려움

 

 

7. Memory Consistency

- 중간에 interleaving 가능함

- 출력이 가능한 값

  : 메모리의 일관성에 따라 다름

  : 하드웨어가 concurrent한 접근을 처리하는 방법에 대한 추상적인 모델

 

- Sequential consistency (순차 일관성)

  : 전반적인 효과는 각 쓰레드와 일치함

  : 그렇지 않으면 arbitrary interleaving

 

- 불가능한 출력 값

 : 100, 1 and 1, 100

 -> Wa와 Wb전에 Ra와 Rb에 동시에 도달해야함

 

 

8. Non-coherent Cache Scenario

 

 

각 쓰레드에는 a, b의 카피본이 존재함

T1에서 a = 2로 만들고 , T2에서 b = 200으로 만들지만 메인 메모리에 최종 업데이트를 하지 않았기 때문에

업뎃 이전의 값을 출력하게 됨

 

 

9. Snoopy Caches

각 cache 블럭에 state를 태그함

: Invalid -> Cannot use value

: Shared -> Readable copy

: Exclusive -> Writeable copy

 

캐시가 E로 태그된 블럭의 요청을 봤을 때

-> 캐시로 부터 값을 공급받음

-> 태그를 S로 변경

 

 

-> 출력시

print 2

print 200

'SP' 카테고리의 다른 글

[sp] Dynamic Memory Allocation (2)  (0) 2022.05.23
[sp] Dynamic Memory Allocation (1)  (0) 2022.05.19
[sp] Synchronization: Advanced (2)  (0) 2022.05.17
[sp] Synchronization: Advanced  (0) 2022.05.05
[sp] Synchronization: Basics  (0) 2022.05.03