평소 주변 동료분들 중에 Go 언어를 좋아하시는 Gopher 분들이 계셔서 Go 언어에 대한 장점들을 익히 들어왔는데
최근 관심이 생긴 회사에서 Go를 사용하고 있어 이번에 Go 언어는 무엇인지에 대해 알아보고자 합니다.
( 온전히 기술적인 측면에서 Go를 탐구하고 배경이나 역사 같은 건 넘어가도록 하겠습니다 )
1. Go는 무엇이 좋은가?
1-1. 단순성(Simple)
Go는 문법이 간결하고 명확하여 러닝커브가 낮습니다.
Go의 예약어는 25개로 Python이 36개, Java가 53개의 예약어가 있는 것을 고려하면 굉장히 적은 것을 알 수 있습니다.
이처럼 Go는 불필요한 복잡성을 최대한 제거하고 코드 가독성을 높이도록 위해 설계되었습니다.
1-2. 병행성(Concurrency)
Go는 고루틴(goroutine)을 통해 병행 프로그래밍을 쉽게 구현할 수 있는 강력한 기능을 제공합니다.
고루틴(goroutine)은 경량의 스레드로, Go Runtime을 통해 많은 수의 고루틴을 효율적으로 실행할 수 있습니다.
Go Runtime과 고루틴에 대한 자세한 내용은 이후에 다루겠습니다.
1-3. 성능(Performance)
Go는 컴파일된 언어로, 실행 속도가 빠릅니다.
Go의 실행 파일은 종속성이 적고, 효율적인 메모리 관리와 가비지 컬렉터(GC)를 통해 성능을 최적화합니다.
또한, Go의 빌트인 프로파일링 도구와 벤치마킹 기능을 통해 성능 최적화 작업을 쉽게 수행할 수 있습니다.
1-4. 표준 라이브러리(Standard Library)
Go는 광범위하고 강력한 표준 라이브러리를 제공합니다.
네트워킹, 웹 서버, 암호화, 파일 I/O 등 다양한 기능을 내장하여 추가 라이브러리 없이도 많은 작업을 수행할 수 있습니다.
한마디로 정리하자면 Go는 기존 프로그래밍 언어에서 불필요하게 복잡한 부분들을 모두 제거하여
성능과 단순함을 챙긴 언어라고 볼 수 있습니다.
이런 부분이 많은 사람들이 Go 언어를 좋아하는 이유가 아닐까 싶습니다.
2. Go가 처리 성능에서 퍼포먼스를 내는 방법
Go에 대해 얕게나마 공부해봤을 때 Go의 가장 두드러지는 특징은 고루틴(Goroutine)인 것 같습니다.
고루틴이란 경량의 스레드로 2kb 정도의 공간을 차지하는 굉장히 작은 실행 단위입니다.
일반적인 스레드가 256kb ~ 2mb 정도의 스택 메모리를 차지하는 것에 비하면 얼마나 작은 크기인지 알 수 있습니다.
이런 고루틴들은 하나의 스레드에서 동시에 여러 개가 실행될 수 있기 때문에 I/O 바운드 작업에 대해 매우 효율적인 처리가 가능해집니다.
오늘날의 서버 성능은 CPU 바운드 작업보다는 I/O 바운드 작업을 얼마나 빠르고 효율적으로 처리하냐에 따라 결정되기 때문에
고루틴의 이러한 설계는 훌륭한 서버 퍼포먼스를 보여주게 됩니다.
하지만 이런 비동기 처리는 Go에서만 제공하는 기능은 아닙니다.
제가 자주 사용하는 Python에서도 비동기 처리를 위해 asyncio를 제공하고 있고
코루틴 객체들을 단일 스레드에서 이벤트 루프가 돌면서 처리하는 구조로 설계되어 있습니다.
Go에서는 고루틴 처리를 위해 Go Runtime 내부에 있는 Goroutine Scheduler가 효율적으로 스케줄링을 수행해 줍니다.
아래 그림은 고루틴이 실행되는 과정을 도식화한 그림입니다.
그림은 OSI 7 계층을 유저 영역, 커널 영역, 하드웨어 영역으로 나누어
가장 상위 계층에서 만들어진 Goroutine이 가장 하위 계층의 CPU로 실행되기까지의 과정을 그리고 있습니다.
2-1. 유저 영역
먼저 응용 프로그램에서 여러 고루틴들이 생성됩니다.
Goroutine Scheduler는 이러한 고루틴들을 LRQ(Local Runnable Queue)라고 불리는 이벤트 큐 같은 공간에 스케줄링을 시켜주어 실행되기까지 대기하도록 만들어 줍니다. 만약 LRQ에 들어가지 못한 고루틴이 있다면 GRQ(Global Runnable Queue)에 넣어줍니다.
그럼 프로세스가 LRQ 혹은 GRQ에 쌓여있는 고루틴을 가져온 후 처리가 가능한 상태의 스레드들이 고루틴들을 가져옵니다.
2-2. 커널 영역
커널 영역에서는 OS Scheduler가 스레드들의 실행을 CPU에서 효율적으로 실행될 수 있도록 스케줄링합니다.
2-3. 하드웨어 영역
하드웨어 영역에서는 CPU가 최종적인 연산을 수행합니다.
Go는 위 단계를 통해 여러 고루틴 객체가 동시에 처리되어 높은 퍼포먼스를 보장합니다.
3. 마무리
Go 언어를 공부하고 이렇게 내용을 정리해 보았는데
평소 Python을 주로 사용해 오던 저에게는 Goroutine이라는 것이 굉장히 신선했습니다.
하지만 멀티스레드로 동작하기 때문에 동시성과 병렬성 이슈를 신경 써줘야 하는데
Go에서는 이를 Channel이라는 것과 여러 다른 방법을 통해 다룬다고 합니다.
이밖에도 Go의 GC(Garbase Collector)와 컴파일 과정도 알아보고 싶었지만 관련 레퍼런스가 많지 않아 정확한 정보를 얻는 것에 어려움이 있었습니다.
추후 Go를 사용하게 되는 계기가 생긴다면 이런 부분들에 대해서도 다뤄보겠습니다!
'개발공부' 카테고리의 다른 글
PostgreSQL에서 PK로 UUID를 사용할 때 고려해야 하는 성능 이슈 (1) | 2024.07.22 |
---|---|
PostgreSQL의 Lock (0) | 2024.06.28 |
동시성 이슈가 발생하는 이유 (0) | 2024.06.26 |
Python List 내부 뜯어보기 (0) | 2024.02.18 |
Python에서 N+1을 해결하는 방법 (SQLAlchemy) (0) | 2024.01.28 |