빠른 결론
- goroutine, 자바 Thread, OS Thread 모두 동시성(Concurrent) 처리를 구현한다.
- 이들의 차이를 세가지 요소 (메모리 소비 / 설치와 철거 비용 / Context Switching 비용)의 관점에서 볼 수 있다.
- 고루틴은 메모리와 소비, 설치와 철거 비용, 컨텍스트 스위칭 비용이 자바 스레드에 비해 저렴하다.
- Thread는 유저 레벨 스레드 / 커널 레벨 스레드로 볼 수 있다.
- 자바의 Thread는 유저 레벨 스레드이지만 커널 스레드로 1:1 매핑되는 방식으로 동작한다.
내용
동시성 프로그래밍을 다루기 위해서 Go는 고루틴, 자바는 쓰레드를 활용합니다. 고루틴의 개념을 빠르게 이해하기 위해서는 가벼운 스레드라고 이해할 수 있습니다.
관리
고루틴은 기본적으로 Go 런타임이 자체 관리합니다. Go 런타임 상에서 관리되는 작업 단위인 여러 Goroutine은 OS 쓰레드와 1:1 매핑되지 않고, 훨씬 적은 OS 쓰레드를 사용합니다.
하지만 자바의 스레드는 OS의 커널 스레드와 1:1 매핑됩니다.(초기의 JVM에서는 JVM이 직접 스케줄링 해 줬었지만, 현재는 OS의 스케줄링 정책을 따릅니다).
User Level 스레드 : Kernel Level 스레드
- N:1방식 (여러 user-level 스레드가 하나의 OS 스레드 위에서 돌아갑니다.)
- 컨텍스트 스위칭이 빠르지만, 멀티코어를 활용할 수 없습니다.
- 1:1 자바가 사용하는 방식으로 1개의 스레드는 1개의 OS 스레드와 일치합니다.
- 멀티코어를 활용할 수 있지만, 컨텍스트 스위치 속도가 느립니다.
3. M:N 여러개의 OS스레드 위에 여러개의 흐름을 갖습니다.
- 구현이 어렵지만, 컨텍스트 스위치 속도도 빠르고 멀티코어도 활용할 수 있습니다.
결국 Go는 M:N 모델을 택하고, 구현의 어려움을 언어 차원에서 구현하여 제공함으로 해결했습니다.
메모리
고루틴의 생성에는 2kb의 스택공간만 필요로 합니다.
반면 쓰레드는 1Mb(500 * 2Kb)로 시작합니다.
웹 서버를 만든다고 할 때, 요청당 1개의 스레드를 만들게 된다면 스레드의 경우 OutOfMemoryError를 만나게 되거나, 메모리 공간을 넘어 가상 메모리에 페이징 되면서 성능 저하를 겪습니다.
설치와 철거
쓰레드는 OS로 부터 리소스를 요청하고 작업이 끝나면 리소스를 돌려주기 때문에 설치, 철거 비용이 큽니다. 이 문제의 해결방법 중 하나로 쓰레드 풀을 사용합니다.
고루틴은 런타임에서 설치(생성), 철거(파괴)작업 비용이 작습니다. 그렇기 때문에 Go는 고루틴들의 관리를 따로 지원하지 않습니다.
Context Switching 비용
고루틴은 교체가 일어날 때 3개의 레지스터(PC, SP, DX)만 저장,복원됩니다.
하지만 쓰레드는 16개의 범용 레지스터, 16개의 XMM 레지스터, 16개의 AVX 레지스터 등을 저장,복원해야 하기에 비용이 많이 듭니다.
- 고루틴의 Context 스위칭은 다음의 경우 이루어집니다.
(unbuffered 채널에 접근할 때, 시스템 I/O가 발생할 때, 메모리가 할당되었을 때, time.Sleep()코드가 실행될 때, runtime.Gosched()가 실행될때)
출처 및 참조
https://blog.nindalf.com/posts/how-goroutines-work/
https://medium.com/@unmeshvjoshi/how-java-thread-maps-to-os-thread-e280a9fb2e06
https://brownbears.tistory.com/313
http://tleyden.github.io/blog/2014/10/30/goroutines-vs-threads/
'Developer > Langauge' 카테고리의 다른 글
GC (Garbage Collection)에 대해. (0) | 2020.11.04 |
---|