이전 글에서 Timely Dataflow를 위한 전송 프로토콜을 왜 새로 설계해야 하는지, 그리고 5개 레이어 구조의 밑그림을 그렸습니다. 이번 글에서는 그 설계의 한 축이 될 수 있는 Apache Arrow를 살펴보고, 구체적으로 어디에 어떻게 적용할 수 있을지 계획을 세워봅니다.

Apache Arrow란 무엇인가

Apache Arrow[1]는 언어 중립적인 컬럼 기반 인메모리 데이터 포맷입니다. 핵심 아이디어는 단순합니다: 데이터를 행(row) 단위가 아니라 열(column) 단위로 메모리에 배치하면, 분석 워크로드에서 극적인 성능 향상을 얻을 수 있다는 것입니다.

행 기반 (Row-oriented):             컬럼 기반 (Column-oriented):
┌──────┬──────┬──────┐             ┌──────┬──────┬──────┐
│ key₁ │ ts₁  │ diff₁│             │ key₁ │ key₂ │ key₃ │  ← keys 버퍼
├──────┼──────┼──────┤             ├──────┼──────┼──────┤
│ key₂ │ ts₂  │ diff₂│             │ ts₁  │ ts₂  │ ts₃  │  ← timestamps 버퍼
├──────┼──────┼──────┤             ├──────┼──────┼──────┤
│ key₃ │ ts₃  │ diff₃│             │ diff₁│ diff₂│ diff₃│  ← diffs 버퍼
└──────┴──────┴──────┘             └──────┴──────┴──────┘

컬럼 기반 레이아웃의 이점은 세 가지입니다.

  1. 캐시 친화성. diff 컬럼만 합산할 때, 행 기반에서는 keyts를 건너뛰며 캐시 라인을 낭비하지만, 컬럼 기반에서는 diff 값만 연속으로 읽습니다. 순차 스캔에서 약 25배의 캐시 효율 차이가 발생합니다.

  2. SIMD 벡터화. 동일 타입의 값이 연속 메모리에 있으면 AVX2/AVX-512 명령어로 한 번에 8~16개 값을 처리할 수 있습니다. Arrow의 compute 커널은 산술 연산에서 4~8배, 비교 연산에서 5~10배의 SIMD 가속을 달성합니다.

  3. 직렬화 제거. Arrow 포맷 자체가 와이어 포맷이 되므로, 프로세스 간 데이터 교환 시 직렬화/역직렬화가 필요 없습니다. IPC 공유 메모리 경로에서 10μs 지연으로 0회 복사 전달이 가능합니다.

Arrow의 메모리 레이아웃은 정밀하게 규격화되어 있습니다. 모든 버퍼는 64바이트 캐시 라인 경계에 정렬되고, 각 컬럼은 유효성 비트맵(validity bitmap) + 오프셋 버퍼 + 데이터 버퍼의 3중 구조를 가집니다. 이 규격의 총 메모리 오버헤드는 약 3%(정렬 패딩 1.6% + 유효성 비트맵 1.25%)입니다.

왜 Arrow인가 — Differential Dataflow의 관점에서

Differential Dataflow[2]의 핵심 자료구조는 델타 배치(delta batch)입니다. (data, time, diff) 트리플의 모음이며, 이것이 워커 간에 교환되고, 컴팩션(consolidation)을 거쳐 누적됩니다.

현재 Timely Dataflow의 Rust 구현체는 이 델타 배치를 Abomonation[3]으로 직렬화합니다. Abomonation은 Rust 구조체의 메모리 레이아웃을 그대로 바이트 배열로 변환하는 제로카피 라이브러리입니다. 역직렬화가 사실상 transmute(바이트 재해석)이므로, 속도는 경이적입니다 — 직렬화 62,717 MB/s, 역직렬화 4,108,000 MB/s.

하지만 Abomonation에는 구조적 한계가 있습니다.

  • 안전성. unsafe 기반의 transmute로, 정의되지 않은 동작(UB)의 위험이 있습니다[4].
  • Rust 전용. C, Python, Java 등 다른 언어에서 Abomonation 포맷을 읽을 수 없습니다.
  • 스키마 진화 불가. 필드 하나를 추가하면 와이어 포맷이 깨집니다.
  • 컴팩션 비효율. 행 기반 레이아웃이라 diff 컬럼만 합산하려 해도 전체 행을 순회해야 합니다.

Arrow는 이 문제들을 해결합니다. 특히 컴팩션 — Differential Dataflow에서 CPU 시간의 상당 부분을 차지하는 연산 — 에서 컬럼 기반 레이아웃의 이점이 큽니다.

47행의 경계 — Arrow가 이기는 지점과 지는 지점

Arrow가 Abomonation보다 항상 나은 것은 아닙니다. 배치 크기에 따라 명확한 교차점이 존재합니다.

Arrow RecordBatch에는 고정 비용이 있습니다: 스키마 메타데이터, 버퍼 정렬 패딩, 유효성 비트맵. 이 고정 비용은 배치가 클수록 행당 비용으로 희석되지만, 배치가 작으면 지배적이 됩니다.

배치 크기 Arrow (행당) Abomonation (행당) 비율
1행 1,280 B 96 B Arrow 13.3x 큼
10행 224 B 96 B Arrow 2.3x 큼
47행 96 B 96 B 교차점
100행 82 B 88 B Arrow 7% 작음
1,000행 70 B 88 B Arrow 20% 작음
1,000,000행 69 B 88 B Arrow 22% 작음

47행이 교차점입니다. 이보다 작으면 Abomonation이 작고, 이보다 크면 Arrow가 작습니다.

Differential Dataflow의 델타 배치는 이중 모드(bimodal) 분포를 따릅니다. 초기 적재(bulk load)에서는 수백만 행의 배치가 생성되고, 증분 업데이트에서는 1~100행 수준입니다. 이 분포의 두 봉우리가 교차점의 양쪽에 놓인다는 점이 핵심입니다.

이것은 두 가지 중요한 결론을 암시합니다:

  1. 핫 패스(증분 업데이트, 1~100행): Abomonation 스타일의 행 기반 포맷이 여전히 유리합니다.
  2. 콜드 패스(컴팩션된 스파인, 1,000행 이상): Arrow 컬럼 포맷이 공간 효율과 분석 성능 모두에서 이깁니다.

따라서 하이브리드 전략이 필요합니다.

하이브리드 설계: 행과 열의 공존

델타 배치의 생애주기와 포맷 전환 핫 패스 (행 기반) 오퍼레이터 출력 → 델타 배치 (1~100행) packed struct · Abomonation 스타일 지연 최소화: 직렬화 0ns 컴팩션 경계 행 → 컬럼 변환 (Arrow RecordBatch) 배치 ≥ 47행일 때 Arrow가 유리 콜드 패스 (컬럼 기반) 컴팩션된 스파인 · Arrow RecordBatch SIMD 컴팩션 · Dictionary 인코딩 공간 22% 절감, 분석 10~100x 가속 와이어 포맷 (UDP) L1~L3: 16B packed header (C) L4 페이로드: Arrow IPC 또는 packed struct (배치 크기에 따라) FPGA: packed struct → DMA 직접 전달 프로세스 간 교환 Arrow IPC (mmap) · 0 복사 C Data Interface로 Rust ↔ C 전달 영속 저장 Arrow IPC / Parquet · 체크포인트

핵심 원칙은 이것입니다: 포맷 전환은 컴팩션 경계에서 한 번만 일어난다. 핫 패스에서는 packed struct의 제로카피 속도를 유지하고, 컴팩션을 거쳐 배치 크기가 충분히 커진 시점에서 Arrow 컬럼 포맷으로 전환합니다.

Differential Dataflow와 Arrow의 계층 구조

Arrow를 Timely Dataflow 프로토콜 스택에 통합하면, Differential Dataflow와의 관계는 다음과 같은 계층 구조가 됩니다.

Differential Dataflow 증분 계산 엔진 · (data, time, diff) 컬렉션 · 컴팩션 · 조인 Rust Timely Dataflow 데이터플로우 런타임 · 프론티어 · 워커 스케줄링 · 진행 추적 Rust L5: Dataflow Binding Rust FFI (extern "C") · Communication trait 구현 Rust L4: Delta Batch Layer + Arrow nanoarrow C · RecordBatch 변환 · SIMD 컴팩션 · Dictionary 인코딩 C Arrow C Data Interface L3: Progress 프론티어 · 포인트스탬프 C L2: Channel 멀티플렉싱 · 배압 C L1: Wire Layer UDP · DPDK PMD · FPGA SmartNIC · 16B packed header C 하드웨어 (FPGA / NIC)

Arrow는 L4 (Delta Batch Layer)에 위치합니다. L1~L3은 이전 글에서 설계한 대로 순수 C로 유지합니다 — 이 레이어들은 FPGA HLS 합성 대상이고, Arrow의 컬럼 포맷과는 독립적으로 동작합니다. L5는 Rust FFI로 Differential Dataflow와 연결합니다.

L4와 L5 사이의 인터페이스는 Arrow C Data Interface[5]가 담당합니다. ArrowArray(88바이트)와 ArrowSchema(72바이트) 두 개의 C 구조체만으로 Rust와 C 사이에서 RecordBatch를 복사 없이 전달합니다.

C 구현체의 선택: GLib인가, stdlib인가, 아무것도 아닌가

Arrow의 C 구현체를 선택하는 것은 생각보다 복잡한 문제입니다. 선택지가 세 가지 있습니다.

Apache Arrow GLib

Arrow GLib[6]는 Arrow C++ 라이브러리의 GObject 기반 C 바인딩입니다. GLib[7]의 타입 시스템(GObject), 메모리 관리(g_malloc/g_free), 컨테이너(GArray, GHashTable), 에러 처리(GError)를 활용합니다.

장점은 명확합니다. 풍부한 자료구조, 참조 카운팅 기반 메모리 관리, GObject Introspection을 통한 Python/JavaScript 바인딩 자동 생성. GNOME 생태계와의 통합도 매끄럽습니다.

하지만 우리의 맥락에서 치명적인 문제가 있습니다:

  • C++ 의존성. Arrow GLib는 Arrow C++ 위의 래퍼입니다. C++ 런타임, 예외 처리, RTTI가 딸려옵니다. 이전 글에서 C를 선택한 이유 — FPGA HLS 합성, ABI 안정성, 런타임 의존성 제거 — 를 정면으로 위배합니다.
  • GLib 자체의 무게. GLib는 glibc 위에 또 하나의 추상 계층을 올립니다. GMainLoop, GType 등 우리가 필요 없는 인프라까지 포함하면 수 MB의 추가 의존성입니다.
  • HLS 합성 불가. g_object_new(), 가상 함수 테이블, 동적 타입 캐스팅 — GObject의 핵심 메커니즘은 FPGA 로직으로 합성할 수 없습니다.

C stdlib만으로

반대편 극단은 C 표준 라이브러리(stdlib.h, string.h, stdint.h)만 사용하는 것입니다. malloc/free, memcpy, qsort — POSIX 환경이라면 어디서든 컴파일됩니다.

이 접근은 FPGA 경로에서는 이상적이지만, Arrow 포맷의 복잡성을 직접 구현해야 합니다. 가변 길이 버퍼의 오프셋 관리, 중첩 타입의 재귀적 버퍼 할당, Dictionary 인코딩의 해시 테이블 — 이 모든 것을 malloc과 포인터 산술로 처음부터 작성하는 것은 삽질의 냄새가 짙습니다.

Nanoarrow: 제3의 길

Nanoarrow[8]는 이 딜레마의 답이 될 수 있습니다. Apache Arrow 프로젝트의 일부로 개발된, Arrow C Data Interface와 C Stream Interface의 최소 구현체입니다.

핵심 특성:

  • ~100KB. GLib 없이, C++ 없이, 순수 C로 약 100KB입니다.
  • Arrow C Data Interface 호환. ArrowArray, ArrowSchema 구조체를 직접 생산하고 소비합니다.
  • 의존성 없음. C99 표준 라이브러리만 필요합니다.
  • 헤더 온리 모드. 단일 .h 파일로 인클루드 가능합니다.

Nanoarrow가 제공하는 것과 제공하지 않는 것:

기능 Nanoarrow Arrow GLib 직접 구현
RecordBatch 생성/소비 O O 직접
Arrow C Data Interface O O 직접
IPC 읽기/쓰기 O (nanoarrow_ipc) O 직접
Dictionary 인코딩 O O 직접
Compute 커널 (SIMD) X O 직접
FPGA HLS 합성 가능 부분적 X O
GObject/GLib 의존성 X 필수 X
C++ 의존성 X 필수 X
코드 크기 ~100KB ~50MB 가변

Nanoarrow를 L4의 기반으로 사용하고, SIMD 컴팩션 커널은 C로 직접 구현하며, FPGA 오프로드 경로의 가장 안쪽 루프만 순수 C stdlib로 작성하는 것이 현실적인 전략입니다.

#include "nanoarrow/nanoarrow.h"

// Nanoarrow로 델타 배치를 Arrow RecordBatch로 변환
int delta_batch_to_arrow(const delta_tuple_t *tuples, size_t n,
                         struct ArrowArray *out_array,
                         struct ArrowSchema *out_schema) {
    struct ArrowSchemaView schema_view;

    // 스키마 정의: key(binary), time(fixed_size_list<uint32>), diff(int64)
    NANOARROW_RETURN_NOT_OK(ArrowSchemaInitFromType(out_schema, NANOARROW_TYPE_STRUCT));
    NANOARROW_RETURN_NOT_OK(ArrowSchemaAllocateChildren(out_schema, 3));

    ArrowSchemaInit(out_schema->children[0]);
    NANOARROW_RETURN_NOT_OK(ArrowSchemaSetType(out_schema->children[0], NANOARROW_TYPE_BINARY));
    NANOARROW_RETURN_NOT_OK(ArrowSchemaSetName(out_schema->children[0], "key"));

    ArrowSchemaInit(out_schema->children[1]);
    NANOARROW_RETURN_NOT_OK(ArrowSchemaSetType(out_schema->children[1], NANOARROW_TYPE_UINT64));
    NANOARROW_RETURN_NOT_OK(ArrowSchemaSetName(out_schema->children[1], "time"));

    ArrowSchemaInit(out_schema->children[2]);
    NANOARROW_RETURN_NOT_OK(ArrowSchemaSetType(out_schema->children[2], NANOARROW_TYPE_INT64));
    NANOARROW_RETURN_NOT_OK(ArrowSchemaSetName(out_schema->children[2], "diff"));

    // ArrowArray 빌더로 데이터 채우기
    struct ArrowArray tmp;
    NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromSchema(&tmp, out_schema, NULL));
    NANOARROW_RETURN_NOT_OK(ArrowArrayStartAppending(&tmp));

    for (size_t i = 0; i < n; i++) {
        NANOARROW_RETURN_NOT_OK(ArrowArrayAppendNull(&tmp, 0));  // struct validity
        // ... 각 컬럼에 값 추가
    }

    NANOARROW_RETURN_NOT_OK(ArrowArrayFinishBuildingDefault(&tmp, NULL));
    ArrowArrayMove(&tmp, out_array);
    return NANOARROW_OK;
}

UDP 기반 프로토콜에서 Arrow 데이터 전송

이전 글에서 설계한 16바이트 wire_hdr_t 헤더는 그대로 유지합니다. Arrow는 L4 페이로드 포맷으로 동작하며, L1의 UDP 패킷 위에 올라갑니다. gRPC 기반의 Arrow Flight는 고려 대상이 아닙니다 — 데이터센터 내부의 워커 메시에서 gRPC의 HTTP/2 프레이밍과 TLS 오버헤드는 불필요합니다.

대신, Arrow IPC 스트리밍 포맷[9]UDP 위에 직접 실어 보내는 방식을 구상하고 있습니다.

┌─────────────────────────────────────────────────────┐
│ UDP Datagram                                        │
│ ┌──────────────┬──────────────────────────────────┐ │
│ │ wire_hdr_t   │ Payload                          │ │
│ │ (16B, C)     │                                  │ │
│ │ ┌──────────┐ │ ┌──────────────────────────────┐ │ │
│ │ │epoch     │ │ │ 배치 ≥ 47행:                 │ │ │
│ │ │iteration │ │ │   Arrow IPC RecordBatch      │ │ │
│ │ │channel_id│ │ │   (FlatBuffers 메타데이터     │ │ │
│ │ │flags     │ │ │    + 컬럼 버퍼)              │ │ │
│ │ │seq_num   │ │ │                              │ │ │
│ │ │payload_le│ │ │ 배치 < 47행:                 │ │ │
│ │ │checksum  │ │ │   packed struct (기존 방식)   │ │ │
│ │ └──────────┘ │ └──────────────────────────────┘ │ │
│ └──────────────┴──────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘

wire_hdr_tflags 필드에 1비트를 추가하여 페이로드가 Arrow IPC인지 packed struct인지 구분합니다. 수신 측은 이 플래그를 보고 적절한 디코더를 선택합니다.

이 설계에서 Arrow IPC의 FlatBuffers 메타데이터는 한 가지 우려를 만듭니다: 소규모 배치에서 메타데이터 비용이 커질 수 있습니다. 하지만 47행 이상의 배치만 Arrow IPC로 보내므로, 메타데이터 비율은 5% 미만으로 유지됩니다.

MTU(1,500바이트)를 초과하는 RecordBatch의 단편화도 L2 Channel Layer에서 처리합니다. Arrow IPC 메시지를 MTU 크기의 청크로 분할하고, 채널별 시퀀스 번호로 재조립합니다. 이것은 이미 L2가 담당하도록 설계된 영역입니다.

Dictionary 인코딩: Differential Dataflow의 숨겨진 기회

Arrow의 Dictionary 인코딩은 Differential Dataflow에서 예상 외의 큰 효과를 발휘할 수 있습니다.

Differential Dataflow의 많은 워크로드에서 data 키는 제한된 도메인(bounded domain)에서 반복됩니다. 그래프 연산에서 정점 ID, RDF 처리에서 URI 문자열, 로그 분석에서 이벤트 타입 — 고유 값의 수가 전체 행 수보다 훨씬 적습니다.

Dictionary 인코딩은 이런 패턴에서 90% 이상의 저장 공간 절감을 달성합니다. 10개의 고유 키가 1,000,000행에 반복될 때, 일반 인코딩 대비 90.7%를 절감합니다. 반면 키가 대부분 고유한 경우(1,000,000 고유 / 1,000,000 행)에는 오히려 14.7%의 패널티가 발생합니다.

이것은 컴팩션 연산에도 영향을 미칩니다. Dictionary로 인코딩된 키 컬럼에서 같은 키끼리 그룹화(group-by)할 때, 키 비교가 정수 비교로 환원됩니다. 가변 길이 문자열의 바이트 단위 비교 대신 uint32_t 비교 한 번 — 이것은 SIMD로 한 번에 8개씩 처리할 수 있습니다.

컴팩션을 Arrow로 재구현하면

Differential Dataflow의 컴팩션(consolidation)은 핵심 연산입니다: 동일 키의 diff를 합산하고, 합이 0인 엔트리를 제거합니다. 현재 Rust 구현은 정렬 기반 단일 패스(sort → merge → accumulate → filter zero)로 동작합니다.

Arrow로 재구현하면 연산은 다음과 같이 분해됩니다:

  1. Group-by + Sum: hash_aggregate 커널로 (key, time) 기준 그룹화 후 diff 합산
  2. Filter zero: filter 커널로 diff ≠ 0인 행만 선택

이것은 원래의 단일 패스보다 다중 패스(multi-pass)입니다 — 각 커널이 독립적으로 전체 컬럼을 순회합니다. 알고리즘 복잡도만 보면 불리합니다.

하지만 SIMD 벡터화가 이 차이를 상쇄하거나 역전시킬 수 있습니다. Arrow의 hash_aggregate 커널은 AVX2로 한 사이클에 8개의 해시를 계산하고, filter 커널은 비트 마스크로 한 번에 64개 행을 평가합니다. 배치 크기가 1,000행 이상일 때, 다중 패스 + SIMD가 단일 패스 + 스칼라를 이기기 시작합니다.

다만 정직하게 말하면, 이 교차점이 정확히 어디인지는 실측 없이는 알 수 없습니다. 이론적 추정과 실제 성능은 캐시 계층 동작, 분기 예측, 메모리 대역폭 포화 등의 변수에 따라 크게 달라집니다.

Claude Code를 믿을 것인가

솔직한 고백을 하나 하겠습니다. 이 글을 쓰면서, 그리고 프로토콜 설계를 진행하면서, Claude Code의 도움을 상당히 받고 있습니다. Arrow의 내부 구조를 조사하고, 바이트 단위 오버헤드를 계산하고, C 코드 스케치를 생성하는 과정에서 AI 코드 생성 도구를 활용하고 있습니다.

이것은 기묘한 위치에 놓인 신뢰의 문제입니다. 두 가지 차원이 있습니다.

첫째, 정확성. AI가 생성한 코드와 수치를 얼마나 믿을 수 있는가? Arrow의 메모리 레이아웃, 배치별 오버헤드 계산, SIMD 가속 비율 — 이런 것들은 공식 문서와 대조해서 검증할 수 있습니다. 실제로 이 글의 수치 중 상당수는 Arrow 공식 스펙[1]에서 직접 확인한 것입니다. 하지만 “47행”이라는 교차점처럼 여러 가정이 결합된 추정은, 개별 가정이 각각 합리적이더라도 결합된 결론이 현실과 얼마나 가까운지 보장하기 어렵습니다.

둘째, 설계 판단. “Nanoarrow를 쓰라”거나 “하이브리드 전략이 맞다”는 판단은 코드 정확성보다 더 미묘한 영역입니다. AI는 검색된 정보를 합리적으로 종합하는 데 능숙하지만, 프로젝트의 장기적 방향성, 유지보수 부담, 커뮤니티 생태계의 미래 — 이런 것들은 경험과 직관의 영역이고, 현재의 AI가 가장 취약한 부분입니다.

제가 취하는 태도는 이렇습니다: AI를 리서치 어시스턴트로 사용하되, 아키텍처 결정은 제가 내린다. 구체적으로:

  • 수치와 사실 확인: AI가 빠르게 조사하고, 제가 공식 문서에서 교차 검증합니다.
  • 코드 스케치: AI가 초안을 생성하고, 제가 한 줄씩 검토합니다. 특히 C 코드에서 메모리 관리, 정렬, 바운더리 조건은 반드시 직접 확인합니다.
  • 설계 대안 탐색: AI에게 “왜 이 접근이 안 되는가”를 질문하면, 놓쳤던 제약 조건을 발견하는 데 도움이 됩니다.
  • 최종 결정: 하이브리드 전략, Nanoarrow 선택, UDP 직접 전송 — 이 결정들은 AI의 제안을 참고하되, 결국 제가 이해하고 설명할 수 있는 범위에서만 채택합니다.

C 프로토콜 코드에서 AI 생성 코드를 그대로 사용하는 것은 위험합니다. 전송 프로토콜은 모든 엣지 케이스 — 패킷 재조립 실패, 부분 수신, 정렬 위반, 버퍼 오버플로우 — 를 정확히 처리해야 합니다. 이 영역에서 “대체로 맞는” 코드는 프로덕션에서 재앙입니다. AI가 생성한 C 코드는 반드시 단위 테스트, 퍼즈 테스팅, Valgrind/ASan으로 검증한 뒤에만 채택할 수 있습니다.

구현 로드맵

정리하면, Arrow를 Timely Dataflow 프로토콜에 통합하는 로드맵은 다음과 같습니다.

Phase 1: L4 내부 포맷으로 Arrow RecordBatch 도입

  • Nanoarrow를 L4에 통합
  • 델타 배치 → Arrow RecordBatch 변환 함수 구현
  • 47행 이상 배치만 Arrow로 변환, 이하는 packed struct 유지
  • 소프트웨어 전용, FPGA 무관

Phase 2: Arrow 기반 컴팩션

  • diff 컬럼 합산을 SIMD C 커널로 직접 구현 (Nanoarrow에는 compute 커널 없음)
  • Dictionary 인코딩 적용하여 키 비교 가속
  • 교차점 실측: 단일 패스 스칼라 vs 다중 패스 SIMD

Phase 3: Arrow IPC를 UDP 와이어 포맷으로

  • Arrow IPC 스트리밍 포맷을 L4 페이로드로 채택
  • L2에서 MTU 단편화/재조립
  • wire_hdr_t.flags에 Arrow IPC 플래그 추가
  • 워커 간 통신에서 직렬화 비용 제거

Phase 4: FPGA DMA 경로의 Arrow 연동

  • Arrow의 64바이트 정렬 버퍼를 DMA 버퍼로 직접 사용
  • FPGA에서는 컬럼 → 행 전치(transpose) 수행 (라인 레이트)
  • Nanoarrow 기반 DMA 디스크립터 생성

마치며

Apache Arrow는 Timely Dataflow 프로토콜의 L4 Delta Batch Layer에 자연스럽게 들어맞습니다. 컬럼 기반 레이아웃은 컴팩션과 분석에서 명확한 이점을 제공하고, C Data Interface는 Rust와 C 사이의 제로카피 브릿지가 됩니다. Nanoarrow라는 경량 C 구현체의 존재가 “GLib도 C++도 없이 순수 C로”라는 제약 조건을 충족시킵니다.

다만 Arrow가 만능은 아닙니다. 47행 미만의 소규모 배치에서는 Abomonation이 여전히 우위이고, 컴팩션의 다중 패스 오버헤드가 SIMD 가속으로 실제 얼마나 상쇄되는지는 실측이 필요합니다. FPGA 경로에서 컬럼-행 전치의 비용도 아직 미지수입니다.

그래서 이 글의 결론은 “Arrow를 쓰자”가 아니라, “Arrow를 이 경계 안에서 쓰자”입니다. 핫 패스는 packed struct, 콜드 패스는 Arrow, 그 사이의 전환점은 컴팩션 경계 — 이 하이브리드 설계가 두 세계의 장점을 취하는 길입니다.

다음 글에서는 Nanoarrow 기반의 L4 프로토타입 구현과, 실제 델타 배치에서의 성능 측정 결과를 다룰 예정입니다.


참고 문헌

  1. Apache Arrow Authors, “Apache Arrow Columnar Format Specification.” 2024. Available at: https://arrow.apache.org/docs/format/Columnar.html</span>
  2. F. McSherry, D. G. Murray, R. Isaacs, and M. Isard, “Differential Dataflow,” in Proceedings of the 6th Biennial Conference on Innovative Data Systems Research (CIDR ’13), CIDR, Jan. 2013.</span> [Link]
  3. F. McSherry, “Abomonation: A mortifying serialization library for Rust.” 2015. Available at: https://github.com/TimelyDataflow/abomonation</span>
  4. RustSec Advisory Database, “RUSTSEC-2021-0120: abomonation transmutes references to and from byte slices.” 2021. Available at: https://rustsec.org/advisories/RUSTSEC-2021-0120.html</span>
  5. Apache Arrow Authors, “Arrow C Data Interface.” 2024. Available at: https://arrow.apache.org/docs/format/CDataInterface.html</span>
  6. Apache Arrow Authors, “Apache Arrow GLib (C API).” 2024. Available at: https://arrow.apache.org/docs/c_glib/</span>
  7. GNOME Project, “GLib Reference Manual.” 2024. Available at: https://docs.gtk.org/glib/</span>
  8. D. Dunnington and Apache Arrow Authors, “nanoarrow: Helpers for Arrow C Data and C Stream Interfaces.” 2024. Available at: https://github.com/apache/arrow-nanoarrow</span>
  9. Apache Arrow Authors, “Arrow IPC Streaming Format.” 2024. Available at: https://arrow.apache.org/docs/format/IPC.html</span>

관련 글