8P by GN⁺ 1일전 | ★ favorite | 댓글 2개
  • jemalloc 의 개발을 주도한 Jason Evans가 직접 쓴 회고록으로, 약 20년에 걸친 jemalloc 개발의 여정을 5단계로 나누어 돌아보며, 성공과 실패, 오픈소스 프로젝트의 한계와 기업 내 변화로 인한 쇠퇴 과정을 진솔하게 기록한 글
  • jemalloc 메모리 할당기는 2004년부터 시작되어 약 20년간 널리 사용되었지만, 최근 Meta의 내부 변화로 인해 공식적 개발이 중단됨
  • 초기 FreeBSD 통합, Firefox 성능 문제 해결, Facebook의 대규모 채택 등 여러 단계에서 성능 최적화와 포팅 경험을 축적함
  • 저장 공간 단편화 문제와 확장성 문제 등 다양한 도전을 거치면서, 성능 향상 및 통계 수집, 테스트 인프라 등 다양한 기능이 추가됨
  • Meta로의 기업 문화 변화와 함께 기술 투자 감소, 핵심 유지보수 인력 부재로 장기 개발이 중단됨
  • Valgrind 제거나 외부 사용자 피드백 부재 등 오픈소스 생태계에서의 단절이 구조적 한계로 작용했음
  • 앞으로 포크(fork) 를 통한 새로운 발전 가능성은 열려 있으나, 공식적인 메인 개발은 더 이상 진행되지 않을 전망임

jemalloc 개요

  • jemalloc은 2004년 처음 고안된 오픈 소스 메모리 할당기로, 성능과 확장성 개선을 목표로 개발되어 20년간 주요 오픈 소스 프로젝트 및 빅테크 인프라에서 활발히 활용됨
  • 최근 메인 개발이 중단되어, 앞으로는 포크나 개별 조직 차원의 유지 관리가 예상됨

Phase 0: Lyken

  • 2004년 Lyken이라는 과학 컴퓨팅용 프로그래밍 언어 개발 과정에서 수동 메모리 할당기부터 시작함
  • Lyken 내부의 할당기는 2005년 5월 기능상 완성됨
  • 할당기는 이후 FreeBSD에 통합되면서, Lyken에서는 시스템 할당기의 얇은 래퍼만 남기고 제거됨
  • 제거 이유는 FreeBSD 통합 후, 시스템 할당기 부족한 부분(스레드별 할당량 추적)만 명확해졌기 때문임
  • 재미있게도, 나중에 jemalloc에 Lyken 시절에 필요로 했던 통계 수집 기능이 추가됨

Phase 1: FreeBSD

  • 2005년 멀티프로세서 컴퓨터가 보편화되던 시기 FreeBSD는 phkmalloc을 사용 중이었지만, 병렬 스레드 환경에 적합하지 않았음
  • Lyken 할당기가 확장성 개선에 명확한 이점이 있어, 이를 FreeBSD에 통합하면서 곧 jemalloc이라는 이름이 붙음
  • 그러나 KDE 애플리케이션 등에서 심각한 단편화 문제가 발생하여 생존 가능성이 의심받음
  • 단편화 원인은 크기 구분 없는 통합 공간 할당 방식 때문이었으며, 연구 끝에 크기별 구역 분리 알고리듬으로 구조를 대폭 변경함
  • 이 과정 내용은 2006년 BSDCan 논문에 기록됨

Phase 1.5: Firefox

  • 2007년 Mozilla Firefox 3 출시를 앞두고 Windows 환경에서 메모리 단편화가 큰 이슈였음
  • jemalloc을 Linux에 포팅하는 일은 쉬웠으나 Windows는 까다로웠음
  • FreeBSD libc에 저장된 jemalloc을 Mozilla에서 포크하여 이식성과 호환성 개선을 진행함
  • 시간이 흐르며 Mozilla가 jemalloc 업스트림에 많은 개선을 기여했으나, 항상 포크 버전이 더 높은 성능을 보임
  • 이는 성능 회귀 문제 때문인지, 특정 환경에 맞춘 최적화 때문인지는 불명확함

Phase 2: Facebook

  • 2009년 Facebook 입사 시, jemalloc의 최대 장애 요인은 도구 미비(프로파일링, 리크 탐지)의 문제였음
  • 이를 보완하여 pprof 호환 힙 프로파일링 기능을 jemalloc 1.0.0에서 도입함
  • 개발은 Github로 이관 후, 사용자 및 외부 기여자와 함께 테스트 인프라, Valgrind 지원, JSON 통계, 새로운 페이지 관리 등 많은 개선이 이루어짐
  • 내부적으로는 Facebook의 거대한 텔레메트리 시스템 덕분에 성능 최적화와 회귀 방지에 큰 도움이 됨
  • 3.x: 테스트 인프라 및 Valgrind 지원 도입
  • 4.x: 디케이 기반 메모리 해제(decay-based purging), JSON 통계 추가
  • 5.x: chunk에서 extent 기반 설계로 변경, 2MiB huge page 활용 기반 마련
  • Facebook 내부의 telemetry 기반 성능 분석이 최적화에 결정적 역할 수행
  • 5.0.0 버전에서 Valgrind 지원 제거는 내부에서 사용하지 않아 결정했으나, Rust 개발자 등 외부에서 반발이 강했음
  • 이후 Facebook/Meta의 조직 변화로 jemalloc 팀이 축소되고, 핵심 기술 투자보다 효율성 중심의 사업 전략으로 전환됨
  • 이에 따라 Huge Page Allocation과 같은 대형 기능의 개발이 정체, 일부 작업은 완결되지 않음
  • 2017년 Evans 퇴사 이후, Qi Wang 주도로 수년간 개발 유지
  • 팀 리더십 이양 후에도 여러 기여자들이 프로젝트 유지해왔으나, 장기적 비전 관리자는 부재하게 됨

Phase 4: Stasis (정지 상태)

  • 현재 jemalloc 업스트림 개발은 종료된 상태로, Meta 역시 내부 필요에 따라 별도의 방향성을 추구하게 됨
  • 기존 코드베이스의 기술 부채가 커서 대대적 리팩토링이 선행 필요함
  • Facebook/Meta의 요구사항과 외부 유저의 요구가 더 이상 일치하지 않음
  • 향후 개발 재개 시 수백 시간 이상의 기술 부채 정리가 선행되어야 하며, 본인은 의욕 없음
  • dev 브랜치 혹은 5.3.0 기준으로 외부 포크는 가능하여, 언제든 포크 기반의 새로운 프로젝트가 등장할 가능성은 있음

회고 및 교훈

  • Valgrind 지원 제거로 생긴 갈등은 외부 사용처 파악 부족에서 기인
  • Android에서 jemalloc이 사용 중이었다는 사실도 2년이 지나서야 알게 됨
  • 프로젝트는 GitHub에서 완전히 공개되었지만, 외부 조직에서의 핵심 기여자가 지속되지 못함
    • Firefox의 Mike Hommey나 CMake 전환 시도도 모두 미완에 그침
  • 경험상, 단순히 오픈만 한다고 지속가능한 독립 프로젝트가 되는 것은 아님
  • 오픈소스는 공개만으로 유지되지 않으며, 핵심 기여자 육성과 거버넌스 확보가 필수적임을 강조

마무리 발언

  • jemalloc은 25년 넘게 가비지 컬렉션 지지자였던 저자에게도 특별한 경험이었음
  • 다시 가비지 컬렉션 시스템 개발에 집중하고 있으나, jemalloc에 협력해준 모든 이들에게 깊은 감사함을 전함
Hacker News 의견
  • 내가 upstream 저장소를 아카이브하기로 한 결정을 이해하는 입장임. Meta를 떠나기 전에는 우리 Jemalloc 팀이 GitHub에 올라오는 온갖 이슈에 대응할 여력이 부족한 상황이었음(예를 들어 누군가 Itanium 환경에서 테스트가 통과하지 않는다고 이슈를 올린 적이 있었는데, 그게 내겐 약간 웃긴 사건이었음). 그래도 이런 상황을 보니 아쉽게 느끼는 감정임. 지금도 jemalloc이 내가 생각할 때 범용 malloc 구현체 중에서 가장 성능이 좋으면서도 쉽게 사용할 수 있는 선택지라고 느낌. TCMalloc도 훌륭하지만, bazel을 사용하지 않는다면 쓰기 정말 힘든 사례라고 생각함(요즘은 bazel 7.4.0에 cc_static_library가 추가되어 정적 라이브러리로 내보내기가 조금 쉬워졌지만, 여전히 그 점은 유의미하게 남아 있음). 나는 Qi에게 저장소를 다시 아카이빙하기 전에 마지막 6.0 릴리스를 한번 만들어줄 수 있는지 물어보려고 마음먹은 상태임. 마지막 릴리스라면 기본 설정을 조금 현대화해도 좋을 것 같다는 생각임. 예를 들어, 이름과 다르게 혼동을 주는 'cache oblivious' 설정을 기본값에서 비활성화해서 16 KiB size-class가 쓸데없이 20 KiB로 불어나는 문제를 막는 게 큰 개선점임. 이전의 선택(즉, Jason의 초기 결정)을 비난하려는 게 아니라, Qi 그리고 David와 당시 논의했을 때 일반적으로 TLB associativity가 지금보다 훨씬 낮았다는 점이 납득할 만한 이유였음. 비슷한 맥락에서, 기본 'page size'를 4 KiB에서 더 큰 값(16 KiB 정도)으로 키우는 것도 좋은 변화가 될 것임. 이러면 대형 size-class의 기준(여러 할당을 슬랩에 할당하다가 일정 크기 이상이면 각각 개별 범위에 할당하는 전환점)이 16 KiB에서 64 KiB로 올라가서 효과가 큼. 내가 Meta를 떠나기 전에 마지막으로 내부 주요 서비스에 이 변경을 적용하려고 검토한 적 있었는데, CPU 사용량이 몇 %나 절감되는 대신 RAM 단편화로 인한 소폭 메모리 증가가 있는 최적화였음. 이 외에도 몇 가지 바꾸고 싶은 점이 있음(예를 들면, metadata_thp 기본값을 'disabled'에서 'auto'로 변경, slabs의 extent 사이징을 페이지 사이즈의 정확한 배수에서 약 1% 낭비를 허용하는 대신 단편화를 줄이는 쪽으로 변경 등). 그래도 앞서 언급한 설정들이 가장 큰 변화가 될 것임

    • Itanium 테스트 스위트 실패 이슈를 등록했던 당사자가 바로 나임

    • 이런 실제 경험담이나 내부자 인사이트가 있어야 Hacker News에 계속 오게 되는 원동력이라고 생각함. TCMalloc을 bazel 없이 쓰기 힘든 이유가 무엇인지 궁금증이 있음(정말로 궁금해서 질문하는 입장임)

    • 이런 중요한 내부 지식이 공식 문서나 블로그 글 같은 확장된 자료로 공개되었으면 하는 바람임. 현재로서는 공식 문서가 너무 빈약하게 느껴지는 상황임. Meta 내부에서 이뤄진 수많은 작업의 노하우가 시간 지나 잊히기 전에 공유됐으면 좋겠다는 생각임

    • 훌륭한 소프트웨어임에도 불구하고 빌드와 통합 과정이 복잡해서 제대로 활용되지 못하는 현실이 좀 아쉽게 느껴지는 상황임

    • ‘Jemalloc 팀이 GitHub에 올라오는 랜덤 이슈 대응할 여력이 충분하지 않았다’는 부분을 언급하셨는데, 궁금한 점이 있음. Meta에 해당 프로젝트를 관리하는 충분한 인원이 있었음에도 이슈 관리는 원활하지 않았던 배경이 무엇인지 알고 싶음. 만약 상황을 잘못 이해하고 있다면 정정해주길 바람

  • Jason의 작업이 우리 업무에 얼마나 큰 영향을 주는지에 대한 이야기를 전하고 싶음. 우리 회사는 수억 건의 이미지/비디오를 매일 처리하는 꽤 규모가 있는 곳임. 시작 초기 몇 년 동안 메모리 단편화 문제로 엄청나게 고생했음. 그러던 어느 날 jemalloc을 도입했고, Dockerfile에서 딱 두 줄만 바꿨을 뿐인데 모든 문제가 해결되는 경험을 했음. 지금은 수백억 매출이 나는 회사로, 모든 서비스와 Dockerfile에 jemalloc을 사용 중임. 정말 마음 깊이 감사함을 전하는 마음임

  • ‘jemalloc이 Rust 바이너리에서 예상보다 빨리 제외됐다’는 의견에 대해, 실제로 그 이슈는 여러 원인 중 하나였던 점을 공유하고 싶음. 이 코멘트에서 관련 배경을 볼 수 있음. 그리고 jemalloc이 완전히 제거된 건 해당 이슈가 처음 제기된 후 무려 2년 뒤였음(참조: 이 PR)

    • 저기에 언급된 여러 이슈 중 arm64에서 하드코딩된 페이지 사이즈 문제가 아직까지도 upstream에서 해결 안 된 부분이 흥미롭게 느껴짐. 이것 때문에 앱 개발자는 여러 개별 arm64 리눅스 바이너리를 따로 제공하거나 일부 플랫폼 지원을 포기해야 하는 상황임. 만약 동적 페이지 사이즈(성능을 위해 ftrace 스타일 바이너리 패치를 동적으로 적용)를 도입했다면 지금보다 많이 느려졌을지 궁금해지는 부분임
  • 내가 수년간 만든 모든 게임 엔진에서 jemalloc을 꼭 쓰는 습관이 됐음. win32 환경에서는 기본 할당자보다 훨씬 빠르고, 모든 플랫폼에서 동일한 할당자를 쓰는 이점도 큼. FreeBSD에서 jemalloc 통합된 걸 계기로 알게 되었고, 그 이후로는 쭉 jemalloc만 사용 중임. jemalloc 덕분에 많은 게임 이용자들이 즐거움을 얻었다고 자부심 느끼는 입장임

    • window의 기본 allocator는 정말 별로라는 생각임. Jemalloc이 최고라는 확신임
  • 좋은 글이라는 인상임 — Facebook(이제는 Meta)이 jemalloc 자체를 더는 쓰지 않는 건지, 아니면 유지보수만 하는 단계인지 궁금함. 혹시 tcmalloc이나 다른 allocator로 전환했을 가능성도 있는지 의문임. ‘Facebook 인프라 엔지니어링에서 코어 기술 투자보다는 ROI 중심으로 방점 이동했다’는 문장이 있었음

    • 내가 Meta를 퇴사한 시점이 거의 2년 전인데(아직도 크게 달라지진 않았을 걸로 예상함), jemalloc은 여전히 Meta 내 모든 바이너리에 정적으로 링크되어 사용 중인 사실임. tcmalloc이나 다른 allocator로 쉽게 전환할 수 있냐는 질문에 대해, jemalloc은 회사 내부 생태계에 아주 깊이 결합되어 있으므로 생각보다 바꾸기 어렵다는 답변임. Strobelight 텔레메트리 플러밍에서부터, jemalloc에 맞춰 사용되는 수많은 extensions(예: 직접 커스텀 extent hook을 사용하는 수동 arena 등), 실제로 대부분의 애플리케이션이 jemalloc의 특성을 최대한 끌어내도록 설계된 진화 과정까지 모두 얽혀 있는 상황임

    • 최근 큰 변화라면 jemalloc의 장기 메인테이너들이 전부 떠난 것임. 하지만 오히려 예전보다 Facebook 내부에서 이 프로젝트에 관심을 더 갖기 시작했고, 최근 일부 논란성 이슈를 거치면서 앞으로 Qi와 Jason, 그리고 외부 유저 입장 모두 고려하는 방향으로 나아갈 수 있길 기대하는 긍정적 전망임

    • Meta는 여전히 자체 포크 버전을 여기서 활발히 개발 중임

  • Firefox부터 Facebook까지 오랜 기간 걸쳐 영향력 있던 이 일을 내가 함께할 수 있어 영광스러웠던 마음임

    • 적절한 시점에 나 또한 이곳에 감사 인사를 남기고 싶은 심정임. 이번 글이 오늘 올라올 거라 예상하진 못했지만, 큰 여정의 작은 부분이라도 함께할 수 있어서 영광이었음. @je, qi, david와 당대 및 이후 기여자들에게도 고마움을 전하고 싶음

    • Facebook 내부에서 코어 기술에 계속 투자할 수 있도록 이끌었던 당신의 리더십이 최대한 결실을 맺었다고 생각함. GraphQL, PyTorch, React 같은 혁신이 가능했던 배경엔 이런 기반이 있었기 때문임

  • “불가능한 선택지 속에서 사람들은 1) 극도의 압박 속에서 안 좋은 결정을 하거나, 2) 극도의 압박 속에서 순응하거나, 3) 우회하는 길밖에 없다”는 FTA 인용문이 직장 분위기라고는 상상하기 힘든 모습임

    • 내가 지난 2008년 이후 경험한 거의 모든 직장이 저런 모습이었다는 점이 씁쓸한 체험담임
  • jemalloc이 macOS 내에서 malloc/free를 LD_PRELOAD처럼 원활하게 오버라이드 할 수 있는 유일한 할당자라고 생각함(2020년경 기준). zone 기반 방식으로 쉽게 기본 할당자로 끼어들 수 있고, Apple 특유의 할당자 요구사항도 잘 맞추고 있음. 다른 third-party allocator들은 이 요구사항 때문에 종종 실패했었음

    • 다만, 이 방식은 macOS 시스템 allocator가 내부 구조를 바꾸지 않는다는 가정 아래에서만 동작할 수 있기 때문에, 실제로 Apple이 변경해서 깨진 사례가 두 번 정도 있었던 걸로 기억함

    • mimalloc도 같은 방식으로 동작할 수 있는 걸로 아는데, 확신은 없음

  • jemalloc이 glibc의 malloc 대비 모든 면에서 개선된 부분이 보였고, 벤치마크 결과를 보면 항상 더 나은 성능을 보여주는 것으로 아는데, 그렇다면 왜 기본 allocator가 아닌지 외부자로서 궁금증이 있었음

    • FreeBSD에서는 이미 jemalloc이 기본임. malloc을 바꾸고 싶다면 libc도 같이 FreeBSD libc로 바꾸는 게 더 쉬울 것이고, 그러려면 kernel도 FreeBSD로 올리는 게 자연스러움. Facebook과 합병될 당시 우리 쪽 직원에게 jemalloc을 소개하면서 신나 했지만, 이미 FreeBSD라서 너무 당연하게 사용하는 문화였음

    • 나는 allocator 엔지니어가 아니라 전문 의견은 아니지만, 예전에 OS allocator를 관리하는 엔지니어와 대화해본적 있음. 그분 말로는, 커스텀 allocator는 한 프로세스가 메모리 할당에서 압도적으로 유리해지는 대신, 시스템 전체의 할당 공정성은 악화되는 경향이 있음. 시스템 allocator 입장에서 개별 프로세스가 패턴을 다르게 가져가면 전체 최적화가 어려워짐. 그래서 대부분의 서비스 환경에서, '지금 내 프로세스만 중요하다'는 전제 하에 jemalloc이 많이 추천되는 것임

    • jemalloc이 기본 할당자가 안될 기술적 이유는 없다고 생각함. 실제로 FreeBSD에서는 디폴트임(기사에서 언급). 내 이해로는 이 이슈는 기술적인 것보다 정치적 성격이 더 크다고 봄

    • jemalloc은 실제 대규모 프로덕션에서 검증됐고 라이선스도 매우 자유롭고 성능도 입증된 사실임. 그런데 glibc malloc을 고수하는 이유가 대체 무엇인지, '이념적 순수성', 레거시 관성 외에 누가 이 상태에서 이득 보는지가 진심 궁금함. 왜 여전히 “호환성 때문”이라고 변명하는지 반문하고 싶은 심정임

    • 예전에는 대안 allocator가 free된 메모리를 절대 OS로 다시 반환하지 않고 dirty page로 들고만 있었다는 문제가 컸음(이건 결국 개선됐지만, allocator의 우선순위 차이를 보여주는 대표 사례임). 그리고 실제로 대부분의 프로세스는 메인 쓰레드 하나만 돌리거나 거의 유휴 쓰레드뿐임. 멀티스레딩 최적화 allocator들은 이런 환경에서는 과잉이고 부담만 될 수 있음. 참고로, 커널이 페이지를 zero 처리하는 비용이나, 유저 프로세스가 자기 내부 재사용을 위해 zero하는 비용이나 실질적으로 차이가 없다는 점을 대부분 생각하지 않는다는 점도 언급하고 싶음

  • 차라리 블로그 글 링크를 github 저장소에 추가해줬으면 하는 바람임. 앞으로 레포 방문하는 사람들이 이 맥락을 알고 참고하는 게 중요하다고 생각함