How Much Memory Do You Need in 2024 to Run 1 Million Concurrent Tasks?
(hez2010.github.io)Rust, C#, Python, Go, Java, NodeJS 등의 최신 버전으로 100만 개의 동시 작업을 처리하는 프로그램을 실행하여 메모리 효율성을 비교했습니다.
C# (NativeAOT)와 Rust가 가장 우수한 메모리 효율을 보였으며, Go는 기대보다 높은 메모리 소비로 낮은 평가를 받았습니다. 전체적으로 .NET과 Rust의 성능이 돋보였고, Java (GraalVM)가 놀라운 개선을 보였습니다.
구체적으로 Rust는 약 29MB로 가장 적은 메모리를 사용했으며, C# NativeAOT는 약 71MB로 뒤를 이었습니다. NodeJS는 232MB, Python은 339MB를 기록했습니다. Go는 753MB로 상대적으로 높은 메모리 사용량을 보여 아쉬운 결과를 나타냈습니다. Java(GraalVM)는 92MB로 큰 개선을 보였습니다.
벤치마크 코드를 보면 Rust와 Python의 경우 실제로는 concurrent task를 만드는 것이 아니라, 비동기적이긴 하지만 다른 task와 parallel하게 돌지는 못하는 future 객체만 생성하는 것으로 보입니다. 아마 C#도 비슷한 케이스가 아닐까 싶네요. 반면 Go 코드는 스스로 call stack 등을 갖는 task인 goroutine을 생성하게 되어 있는데, 100만 개의 케이스에서 Go의 메모리 사용량이 유독 튀어 보이는 원인은 여기에 있지 않나 싶습니다.
Go에 대해 옹호를 하자면 Go는 100만개 도는 함수안에 어떤 라이브러리가 있어도 동작을 합니다. go 만 붙여주면 됩니다. 비동기 기반의 다른언어에서는 중간에 동기방식으로 조금 시간을 잡아먹는 라이브러리가 있으면 이게 비동기의 이점을 다 잡아먹는 미치는 상황이 와버리죠.
비동기의 이점을 100% 누릴려면 조금이라도 시간을 잡아먹는 함수는 다 비동기로 바꿔야 합니다.
자바 VirtualThread는 음... 이번에 우리회사에서 자바 VirtualThread 믿고 가다가 라이브러리 호환성때문에 결국 일반 쓰레드로 바꾸고 인스턴스 수십개 띄우는 엔딩으로 결말을 맞았습니다.
스프링에서 흔히 말하는 "WebFlux를 제대로 사용하려면 JPA말고 R2DBC와 함께 사용해야 진가가 들어난다" 라는 말이 없다고 말할 수 있겠네요.
예시로 주신 msal 라이브러리는 Go의 경우에도 thread safe하지 않은 자료형, 혹은 구조가 사용되는 라이브러리면 마찬가지인 케이스라고 생각됩니다
정보 감사합니다
질문 드리고 싶은게 있습니다
그럼 go를 제외한 다른 언어들은 말씀해주신것처럼 동기방식의 라이브러리가 있으면 다 깨지는걸까요?
아니면 혹시 다른 언어들 중에서도 go처럼 완벽한 비동기를 지원하는 언어가 있는건지 궁금합니다
colorless 란 표현이 있습니다. 비동기랑 동기를 구분지을 필요가 없는 언어는 go 가 유일합니다. 사용자 입장에서 동시성이 필요한 프로그래밍을 할때 난이도, 사용성 측면에서 go는 압도적인 강점이 있습니다.
최적화된 비동기 프로그래밍보다 성능이야 조금 떨어지겠지만요.
지적 죄송한데 깨진다는 표현하고 완벽한 비동기란표현에 틀린 부분만 말씀드릴께요. 비동기에도 동기방식 라이브러리 써도 괜찮습니다 단 짧은시간내에 완료가 된다는 보장이 있으면요 동기방식으로 실행시간이 긴 호출이 있으면 다른 비동기의 처리가 늦어지니 문제가 생기는거고. go 는 고루틴으로 억단위의 작업을 배정해도 되므로 언어자체에 비동기란 개념이 없습니다. 쓰는 사람입장에서는 어떤 함수도 go 만 붙여 호출해주면 병렬로 실행되니 엄청 편한거죠. 완벽한 비동기는 언어자체의 근간이 비동기인 자바스크립트가 아닐까 개인적으로 생각합니다