본문 바로가기
프로그래밍/Desktop App

[시스템 프로그래밍] 쓰레드 동기화와 캐시 알고리즘

by YuminK 2023. 12. 3.

쓰레드의 동시접근 이슈

일반적으로 실행중인 쓰레드의 변경이 라인 단위로 이뤄진다고 생각하지만, 연산을 처리하는 도중에 컨텍스트 스위칭이 일어날 수 있다. 따라서 쓰레드가 같은 메모리 영역을 동시에 접근하는 것은 문제를 일으킬 가능성이 높다. 

 

메모리 접근의 동기화

- 유저 모드 동기화

동기화가 진행될 때, 커널 코드가 실행되지 않는 방식이다. 커널 모드의 전환이 일어나지 않으므로 성능상의 이점이 있다. 다만 그 만큼 기능상의 제한도 존재한다. 

 

- 커널 모드 동기화

동기화와 관련된 처리에서 커널 모드로의 전환이 이루어져서 성능 저하로 이어진다. 그러나 유저 모드 동기화에서 제공하지 못하는 기능을 제공 받는다.

 

- 크리티컬 섹션

한 순간에 하나의 쓰레드만 접근이 요구되는 코드 블럭을 의미한다. 

 

- 유저모드 동기화

 - 크리티컬 섹션: 열쇠 1개를 가지고 들어가는 형태와 유사

 - 인터락 함수: 원자 단위의 실행을 지원하는 함수를 사용하는 방식

 

- 커널모드 동기화

 - 뮤텍스(크리티컬 섹션과 유사)

 - 세마포어: 임계영역에 접근하는 키가 여러 개인 형태이다. 

 

실행 순서의 동기화

 - 생산자/소비자 모델

생산자가 데이터를 입력하면, 소비자는 데이터를 출력한다. 입력을 처리하는 쓰레드와 출력을 처리하는 쓰레드가 있고 중간에 버퍼를 두는 것이다.

 

 - 이벤트 기반 동기화 

 - 타이머 기반 동기화 

 

쓰레드 풀링

한번 생성한 쓰레드를 재활용하여 시스템의 부담을 덜기 위한 방식이다. 쓰레드의 생성과 소멸에는 많은 리소스가 투입된다. 따라서 미리 생성한 쓰레드를 사용하고 재활용하는 방식이다. 

 

메모리 계층 구조

CPU에 가까이 있는 메모리는 빠르고, 먼 메모리는 느리다. 단순히 물리적 거리 때문은 아니고 I/O버스를 통해 데이터를 가져와야 하기 때문이다. 그렇다면 왜 레지스터나 캐시메모리를 작게 하고 하드디스크의 메모리를 크게 만드는가? 이는 기술과 비용면에서 어렵기 때문이다.

 

레지스터 > L1 캐시 > L2 캐시 > 메인 메모리 > 하드디스크 순으로 빠르다. 접근이 많이 필요한 데이터 일수록 높은 계층에 존재한다. 예를 들어 특정 연산에 데이터가 필요하다면 상위 계층부터 확인하면 캐시된 데이터가 있는지 확인한다. 대부분의 연산은 L1, L2 캐시에 필요한 데이터가 이미 있을 확률이 높다. (책에서는 90%이상이라고 한다.)

 

캐시는 연산 속도를 매우 향상 시킨다.

 

템퍼럴 로컬리티

프로그램 실행시 한번 접근이 이루어진 주소의 메모리 영역은 더욱 자주 접근하게 된다.

 

스페이셜 로컬리티

한번 접근한 메모리 근처에 있는 메모리에 접근할 확률이 높다.  

 

캐시 알고리즘

L1 캐시에 해당하는 데이터가 존재할 경우, 캐시 힛(Cache Hit)이 발생했다고 한다. 만약 데이터가 존재하지 않는 경우 캐시 미스(Cache Miss)가 발생했다고 한다. 이후 L2 캐시에서 확인한다. L2에서도 찾을 수 없다면 메인메모리에서 찾는다.

 

만약에 찾는 메모리가 L1 캐시에 존재하지 않은 경우, 상위 계층으로 해당 메모리 블록이 이동하게 된다. (스페이셜 로컬리티의 특성을 활용) 이미 L1 캐시에 존재하는 경우에는 처리되지 않는다. (상위 계층이 레지스터라 더 이상 승격? 시킬 수 없음) 많은 접근이 이뤄지는 메모리 블록은 상위로 올라가므로 템퍼럴 로컬리티의 개념 역시 활용하고 있다. 또한 하위 계층의 블록은 상위 계층보다 더욱 크다. 따라서 접근의 횟수를 작게 가져가는 점도 성능 향상에 도움이 된다.

 

메모리 블록을 교체할 때는 캐시 교체 정책을 따르는데, 일반적으로 LRU(Least-Recently Used) 알고리즘을 사용한다. 

댓글