# 2024년 1백만 동시 작업 실행에 필요한 메모리 용량

> Clean Markdown view of GeekNews topic #18028. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=18028](https://news.hada.io/topic?id=18028)
- GeekNews Markdown: [https://news.hada.io/topic/18028.md](https://news.hada.io/topic/18028.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2024-11-30T09:56:57+09:00
- Updated: 2024-11-30T09:56:57+09:00
- Original source: [hez2010.github.io](https://hez2010.github.io/async-runtimes-benchmarks-2024/)
- Points: 2
- Comments: 1

## Topic Body

### 벤치마크

- **코루틴이란?**
  - 코루틴은 프로그램의 실행을 일시 중지하고 재개할 수 있는 컴퓨터 프로그램 구성 요소로, 협력적 멀티태스킹을 위한 서브루틴을 일반화한 것임.
  - 협력 작업, 예외, 이벤트 루프, 반복자, 무한 리스트 및 파이프와 같은 프로그램 구성 요소 구현에 적합함.

- **Rust**
  - 두 가지 프로그램을 작성함: `tokio`와 `async_std`를 사용한 프로그램.
  - 둘 다 Rust에서 일반적으로 사용되는 비동기 런타임임.

- **C#**
  - C#은 Rust와 유사하게 `async/await`를 지원함.
  - .NET 7부터 NativeAOT 컴파일을 제공하여 VM 없이도 관리 코드 실행이 가능함.

- **NodeJS**
  - 비동기 작업을 위해 `Promise.all`을 사용함.

- **Python**
  - `asyncio` 모듈을 사용하여 비동기 작업을 수행함.

- **Go**
  - 고루틴을 사용하여 동시성을 구현하며, `WaitGroup`을 사용하여 작업을 대기함.

- **Java**
  - JDK 21부터 가상 스레드를 제공하며, 이는 고루틴과 유사한 개념임.
  - GraalVM을 사용하여 네이티브 이미지를 생성할 수 있음.

### 테스트 환경

- **하드웨어**: 13세대 Intel(R) Core(TM) i7-13700K
- **운영체제**: Debian GNU/Linux 12 (bookworm)
- **Rust**: 1.82.0
- **.NET**: 9.0.100
- **Go**: 1.23.3
- **Java**: openjdk 23.0.1
- **Java (GraalVM)**: java 23.0.1
- **NodeJS**: v23.2.0
- **Python**: 3.13.0

### 결과

- **최소 메모리 사용량**
  - Rust, C# (NativeAOT), Go는 네이티브 바이너리로 컴파일되어 적은 메모리를 사용함.
  - Java (GraalVM 네이티브 이미지)도 좋은 성능을 보였으나, 다른 정적 컴파일 언어보다는 더 많은 메모리를 사용함.

- **10K 작업**
  - Rust는 메모리 사용량이 거의 증가하지 않음.
  - C# (NativeAOT)도 적은 메모리를 사용함.
  - Go는 예상보다 많은 메모리를 사용함.

- **100K 작업**
  - Rust와 C#이 좋은 성능을 보임.
  - C# (NativeAOT)이 Rust보다 적은 메모리를 사용함.

- **100만 작업**
  - C#이 모든 언어를 압도하며 가장 적은 메모리를 사용함.
  - Rust도 메모리 효율성이 뛰어남.
  - Go는 다른 언어에 비해 메모리 사용량이 많음.

### 결론

- 많은 동시 작업은 복잡한 작업을 수행하지 않더라도 상당한 메모리를 소모할 수 있음.
- .NET과 NativeAOT의 개선이 눈에 띄며, GraalVM으로 빌드된 Java 네이티브 이미지도 메모리 효율성이 뛰어남.
- 고루틴은 여전히 자원 소비 면에서 비효율적임.

### 부록

- Rust (tokio)에서 `join_all` 대신 `for` 루프를 사용하여 메모리 사용량을 절반으로 줄임. Rust가 이번 벤치마크에서 절대적인 선두를 차지함.

## Comments



### Comment 31864

- Author: neo
- Created: 2024-11-30T09:56:57+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=42270378) 
- 벤치마크가 Node와 Go의 비동기 처리 방식의 차이를 제대로 반영하지 못함. Node는 `Promise.all`을 사용하고 Go는 고루틴을 사용하여 차이가 있음. 비동기 I/O와 CPU 바운드 작업의 메모리 사용량 차이를 비교하는 것이 흥미로울 것임

- "10초 동안 대기하는 작업"과 "10초 후에 깨우는 작업"의 차이를 설명함. Go 코드의 메모리 사용량이 다른 코드와 비교하여 차이가 큼

- Go와 Node의 공정한 비교를 위해 타이머를 스케줄링하는 고루틴과 타이머 신호를 처리하는 고루틴을 사용하는 방법을 제안함. Node에 Bun과 Deno가 포함되지 않은 점이 이상하다고 언급함

- 많은 동시 작업이 메모리를 많이 소비할 수 있지만, 작업당 데이터가 몇 KB 이상이면 스케줄러의 메모리 오버헤드는 무시할 수 있을 정도임

- "동시 작업"의 정의에 따라 메모리 사용량이 달라질 수 있음. 효율적인 구현에서는 1M 동시 작업에 약 200MB가 필요함

- Go가 메모리 사용량에서 Java에 비해 2배 이상 뒤처진다는 점을 지적하며, 벤치마크가 실제 프로그램을 대표하지 않는다고 언급함

- 간단한 코드로 언어를 비교하는 것이 개발자에게 불공평할 수 있으며, 실제 작업을 추가하여 메모리 사용량과 스케줄링 차이를 측정할 것을 권장함

- 벤치마크가 종종 오류로 가득 차 있으며, 이러한 벤치마크를 게시하는 사람들의 동기를 이해하지 못하겠다고 말함

- Java 벤치마크가 잘못되었을 가능성이 있으며, `ArrayList`의 초기 크기를 지정하지 않아 불필요한 객체가 많이 생성됨

- Rust의 비동기 코드가 예상보다 빠르게 완료되는 이유를 설명함. `tokio::time::sleep()`이 미래가 생성된 시점을 추적하기 때문임
