# Protobuf를 제거하고 Rust↔C 직접 바인딩으로 성능 5배 개선

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

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=26253](https://news.hada.io/topic?id=26253)
- GeekNews Markdown: [https://news.hada.io/topic/26253.md](https://news.hada.io/topic/26253.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2026-01-30T12:54:40+09:00
- Updated: 2026-01-30T12:54:40+09:00
- Original source: [pgdog.dev](https://pgdog.dev/blog/replace-protobuf-with-rust)
- Points: 8
- Comments: 3

## Summary

**PgDog**이 SQL 파싱 병목을 해결하기 위해 **Protobuf 직렬화 대신 Rust–C 직접 바인딩**을 도입했습니다. `pg_query_parse_protobuf` 함수의 오버헤드를 제거하고, Claude LLM으로 생성한 6,000줄의 변환 코드를 적용한 결과 파싱은 5.45배, 디파싱은 9.64배 빨라졌습니다. 이로써 PgDog은 PostgreSQL 확장 프록시로서 CPU 사용량과 지연시간을 크게 줄이며, 수평 확장 환경에서의 효율성을 높였습니다.

## Topic Body

- PostgreSQL 확장 프록시인 **PgDog**가 SQL 파싱 성능을 높이기 위해 **Protobuf 직렬화 대신 Rust 직접 바인딩**을 도입  
- 기존 Protobuf 기반 구조를 **C–Rust 직접 변환(bindgen + Claude 생성 래퍼)** 으로 교체해 **파싱 5.45배, 디파싱 9.64배** 속도 향상  
- 성능 병목은 **pg_query_parse_protobuf** 함수에서 발견되었으며, 캐싱 시도 후에도 근본적 개선을 위해 구조 변경 수행  
- Claude LLM을 활용해 **6,000줄의 Rust–C 변환 코드**를 자동 생성하고, `parse`, `deparse`, `fingerprint`, `scan` 등 주요 함수에 적용  
- 이 최적화로 PgDog의 **CPU 사용량과 지연시간이 감소**, PostgreSQL 수평 확장 프록시로서의 효율성이 크게 향상됨  
  
---  
  
### PgDog과 Protobuf의 한계  
- **PgDog**은 PostgreSQL을 확장하기 위한 프록시로, 내부적으로 **libpg_query**를 사용해 SQL 쿼리를 파싱함  
  - Rust로 작성되어 있으며, 기존에는 **Protobuf 직렬화/역직렬화**를 통해 C 라이브러리와 통신  
- Protobuf는 빠르지만, **직접 바인딩 방식이 더 빠름**  
  - PgDog 팀은 `pg_query.rs`를 포크해 Protobuf를 제거하고 **C–Rust 직접 바인딩**을 구현  
  - 결과적으로 쿼리 파싱은 5.45배, 디파싱은 9.64배 빨라짐  
  
### 벤치마크 결과  
- 벤치마크는 PgDog의 포크 저장소에서 재현 가능  
  - `pg_query::parse` (Protobuf): 613 QPS  
  - `pg_query::parse_raw` (직접 C–Rust): 3357 QPS  
  - `pg_query::deparse` (Protobuf): 759 QPS  
  - `pg_query::deparse_raw` (직접 Rust–C): 7319 QPS  
  
### 성능 병목 분석과 캐싱 시도  
- **samply** 프로파일러를 사용해 CPU 사용 시간을 분석한 결과, **pg_query_parse_protobuf** 함수가 병목으로 확인됨  
- 캐싱을 통해 일부 개선을 시도  
  - **LRU 알고리듬 기반 해시맵 캐시**를 사용, 쿼리 텍스트를 키로 AST를 저장  
  - 준비된 문장을 사용하는 경우 재사용 가능  
- 그러나 일부 ORM이 **수천 개의 고유 쿼리**를 생성하거나, **오래된 PostgreSQL 드라이버**가 준비된 문장을 지원하지 않아 캐시 효율이 낮았음  
  
### LLM을 활용한 Protobuf 제거  
- PgDog 팀은 **Claude LLM**을 활용해 Protobuf를 제거한 Rust 바인딩을 생성  
  - 명확하고 검증 가능한 작업 범위 내에서 AI가 효과적으로 작동  
- Claude는 `libpg_query`의 Protobuf 스펙을 기반으로 **C 구조체를 Rust 구조체로 매핑**  
  - 2일간의 반복 작업 끝에 6,000줄의 재귀적 Rust 코드 완성  
- `parse`, `deparse`, `fingerprint`, `scan` 함수에 적용해 **pgbench 기준 25% 성능 향상** 확인  
  
### 구현 세부 구조  
- Rust와 C 간 변환은 **unsafe 함수**를 사용해 구조체를 직접 매핑  
  - C 구조체를 Postgres API에 전달해 AST를 생성하고, Rust로 재귀 변환  
- 각 AST 노드는 **convert_node** 함수로 처리되며, SQL 문법의 수백 개 토큰을 매핑  
  - SELECT, INSERT 등 각 노드 타입별로 별도 변환 함수 존재  
- 변환 결과는 기존 Protobuf 구조체(`protobuf::ParseResult`)를 재활용해 **테스트 시 byte 단위 비교 검증** 가능  
- 재귀 알고리듬은 **메모리 할당이 적고 CPU 캐시 효율이 높아** 반복문 기반 구현보다 빠름  
  - 반복문 기반 구현은 불필요한 메모리 할당과 해시맵 조회로 인해 오히려 느림  
  
### 결론  
- Postgres 파서의 오버헤드를 줄여 PgDog의 **지연시간, 메모리, CPU 사용량**을 모두 절감  
- 이 최적화로 PgDog은 **더 빠르고 저비용으로 운영 가능한 PostgreSQL 확장 프록시**로 발전  
- PgDog은 PostgreSQL의 **수평 확장(next iteration)** 을 함께 구축할 엔지니어를 모집 중임

## Comments



### Comment 50331

- Author: a1eng0
- Created: 2026-01-31T10:40:36+09:00
- Points: 1

제가 원문을 곡해하고 있는 것일지도 모르겠지만, 유독 러스트 관련 글 들은 본질을 떠나 마치 "러스트라서" 빨라진 것 같이 글을 쓰는 것 같아요.  
  
이번 글의 주요 포인트는 불필요한 직렬화 오버헤드를 줄여 성능이 개선된건데 말이죠.  
  
지금 다시 보니 또 그렇게 러스트를 찬양하는 글은 아닌 것 같은데, 다른 글들에 의해 제가 부정적인 인식이 생겨버러셔일까요?

### Comment 50333

- Author: xguru
- Created: 2026-01-31T11:12:27+09:00
- Points: 1
- Parent comment: 50331
- Depth: 1

저도 이거 원문 제목이 실제 내용과 달리 너무 Rust 라서 성능 향상에 초점을 두는 것처럼 보여져서, 살짝 수정했습니다.   
Rust 글들이 그런 경향이 자주 보여서 살짝 필터링하면서 볼 필요는 있는 것 같아요.

### Comment 50260

- Author: neo
- Created: 2026-01-30T12:54:40+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=46730214) 
- 제목이 마치 Rust가 **5배 성능 향상**을 준 것처럼 보이지만, 실제로는 느려졌다는 점이 아이러니함  
  문제는 Rust로 작성된 소프트웨어가 C로 된 libpg_query를 써야 했는데, 직접 연결할 수 없어 **Protobuf 기반의 Rust–C 바인딩**을 사용한 것임  
  이 방식이 느려서, 결국 LLM의 도움으로 비이식성이지만 훨씬 최적화된 바인딩을 새로 작성했음  
  만약 애초에 C로 썼다면 변환 과정이 필요 없었을 것임. 즉, 제목은 “Rust 사용으로 인한 성능 손실을 줄였다”가 더 정확했을 것임  
  변환 계층은 이식성과 보안을 주지만, 결국 **복사·변환·직렬화**가 반복되며 앱을 느리게 만드는 원인 중 하나라고 생각함  
  - Rust가 느린 게 아니라, 외부 라이브러리의 **비효율적 설계**가 문제였음  
    Rust에서 C 라이브러리를 호출하는 건 매우 쉽고, 안전한 래퍼도 이미 많이 존재함  
    Protobuf를 중간에 둔 구조는 거의 본 적이 없으며, 그게 병목이었음  
    제목은 단순히 클릭을 유도하기 위한 “Rust로 다시 썼다”식 밈에 가깝다고 봄  
  - C로 썼다면 더 빠를 거라는 건 공정하지 않음  
    원래 라이브러리가 **직렬화/역직렬화**를 반복하는 잘못된 설계를 하고 있었고, 그걸 제거한 게 핵심임  
    제목은 “Protobuf를 일반 API로 교체해 5배 빨라졌다”가 더 정확함  
  - 왜 바로 FFI를 쓰지 않았는지 궁금함  
    Rust에서 C 바인딩은 가장 쉽고, 큰 API가 아니라면 단순함  
    Protobuf는 메모리 내 데이터 교환에는 **부적절한 도구**라고 생각함  
  - LLM으로 최적화를 했다면, 차라리 C 라이브러리를 Rust로 **완전히 포팅**하는 데 썼으면 어땠을까 생각함  
    앞으로 LLM 덕분에 다양한 언어로의 포팅이 폭발적으로 늘어날 것 같음  
  - Rust와 Postgres 사이에 Protobuf를 둔 건 **성능 악몽** 수준임. 그런 라이브러리가 인기를 얻은 게 놀라움  

- 제목이 다소 오해를 부름  
  사실상 “Protobuf 직렬화 단계를 제거했더니 빨라졌다”는 내용임  
  - Protobuf는 단순 복사로는 불가능한 **버전 호환성**을 제공함  
    클라이언트와 서버가 독립적으로 업데이트돼도 동작하게 해주며, 여러 언어 간 통신을 쉽게 함  
    대규모 시스템에서는 이런 유연성이 매우 중요함  
  - Protobuf 직렬화가 단순 메모리 복사보다 5배 느리다는 건 오히려 **생각보다 빠르다**는 인상임  
  - memcpy나 mmap이 훨씬 빠르지만, Rust 진영에서는 그런 **비안전한 방식**을 꺼림  
  - 이런 경우엔 **Arrow 같은 표준화된 zero-copy 포맷**을 쓰면 좋을 것 같음. 언어별 패딩 문제나 보안 검사를 자동으로 처리해줌  

- Rust 때문이 아니라, Protobuf를 **일반화된 저장 포맷**으로 쓴 게 느린 원인일 수 있음  
  결국 특정 목적에 맞게 단순화한 게 핵심임  
  - “Protobuf를 네이티브 최적화 구현으로 교체했다”는 제목은 주목을 덜 받았을 것임  
    Rust를 제목에 넣은 건 클릭 유도를 위한 선택 같음  
  - 기사 제목이 논란을 부르지만, 본문은 그걸 인식하고 있음  
  - 실제로는 Rust와 거의 관계가 없지만, Rust가 없었다면 **메인 페이지에 오르지 못했을 것**임  

- pg_query의 원 저자가 배경을 설명함  
  원래 pganalyze에서 Postgres 쿼리를 파싱해 테이블 참조를 찾고, 쿼리를 재작성·포맷하는 용도로 사용했음  
  초기엔 JSON을 썼지만, 이후 **Protobuf로 전환**해 여러 언어(Ruby, Go, Rust, Python 등)에서 **타입 안정성 있는 바인딩**을 쉽게 제공하기 위함이었음  
  Rust 같은 언어는 FFI가 낫지만, 다른 언어들은 유지보수 부담이 큼  
  Lev의 시도는 지지하며, 앞으로 [libpg_query에 직접 FFI로 접근할 수 있는 함수](https://github.com/pganalyze/libpg_query/pull/321)를 추가할 예정임  
  다만 성능이 중요하지 않은 경우엔 Protobuf가 여전히 더 **편리한 선택**임  

- “5배 빠르다”는 말이 Cap’n Proto의 “**무한히 빠르다**”는 농담을 떠올리게 함  
  - Cap’n Proto는 Protobuf 제작자가 만든 것으로, **파싱이 필요 없는 구조**라서 그런 표현을 쓴 것임  
  - 하지만 실제로 써보면 Cap’n Proto는 **사용성이 떨어짐**  

- 제목은 과장됐지만, 실제 작업은 인상적임  
  Protobuf를 완전히 없앤 게 아니라 **사용 방식을 최적화**한 것임  
  “X로 바꿨더니 5배 빨라졌다”는 문구는 대개 “엉망이던 구현을 고쳤다”는 뜻임  
  핵심 교훈은  
  1. 직렬화/역직렬화는 **숨은 병목**이 되기 쉬움  
  2. 기본 구현은 대부분 특정 상황에 최적화돼 있지 않음  
  3. **프로파일링**을 통해 병목을 정확히 찾아야 함  
  Rust FFI에도 오버헤드가 있으므로, 진짜 성과는 언어가 아니라 **데이터 흐름 재설계**와 최적화 노력 덕분임  

- FlatBuffers가 더 빠르지만, Protobuf를 쓰는 이유는 **대기업이 유지보수**하기 때문임  
  - 하지만 FlatBuffers도 [Google이 유지보수](https://github.com/google/flatbuffers)함  
    결국 “Google이 만든 것이라 안전하다”는 인식이 근거 없음  
  - 나도 예전에 Google 플랫폼(code.google.com)에 코드를 올렸다가 망한 경험이 있음  
    단순히 **메모리 공유와 버전 필드만 있는 zero-copy 구조**면 충분한데, 굳이 Protobuf를 쓸 이유가 없다고 생각함  
  - Google은 아직도 **문자열 필드 zero-copy 최적화**를 공개하지 않았음  

- Protobuf 성능은 **농담 수준**이라 생각함  
  직렬화가 사실상 무료인 **zero-copy 포맷**을 써야 함  
  예를 들어 내가 만든 [Lite³](https://github.com/fastserial/lite3)는 FlatBuffers보다 242배 빠름  
  - 하지만 그 라이브러리는 **2025년 11월 이후에야 등장**했음  
    Protobuf를 쓰는 이유는 생태계, 스키마, 언어별 툴링 등 수많은 현실적 이유 때문임  

- 실제로는 Rust나 Protobuf 문제가 아니라, PostgreSQL 추상화 계층의 **비효율적 직렬화 구현**이 원인이었음  
  pgdog는 그 계층을 제거하고 C API로 직접 데이터를 전달했음  
  필요 없는 기능을 제거하면 당연히 빨라짐  
  하지만 어떤 사람들에게는 여전히 **직렬화가 필요한 상황**이 있음  
  그런 이들에게 “Rust로 바꾸라”는 제목은 잘못된 메시지임  
  결국 대부분의 경우엔 **JSON이면 충분**하고, 정말로 더 빠른 게 필요하다면 직렬화 자체를 피해야 함  

- 이건 **불공정한 비교**임  
  IPC 통신에 직렬화 프로토콜을 쓰는 건 당연히 오버헤드가 있음  
  “20% 빨라지면 개선, 10배 빨라지면 처음부터 잘못 만든 것”이라는 말이 딱 들어맞는 사례임
