Bluetape4k Graph Part 3: 그래프 입출력과 벤치마크 해석

서비스에서 만든 그래프를 항상 데이터베이스 안에서만 다루지는 않습니다. 데이터 이전, 스냅샷, 분석, 재현 가능한 테스트
데이터를 만들려면 정점과 간선을 파일로 내보내고 다시 불러와야 합니다. 운영 중 발생한 문제를 로컬에서 재현하려면 해당
그래프를 파일로 추출할 수 있어야 합니다. bluetape4k-graph의 graph-io는 이 작업을 데이터 형식별 모듈과 입출력
조합 계층으로 나눕니다.

그래프 데이터 형식 선택
섹션 제목: “그래프 데이터 형식 선택”현재 graph-io에서 구현된 데이터 형식은 CSV, Jackson2 NDJSON, Jackson3 NDJSON, GraphML입니다.
| 형식 | 적합한 용도 |
|---|---|
| CSV | 사람이 직접 확인하고 수정하기 쉬운 소규모 테스트 데이터 |
| Jackson2 NDJSON | Jackson2를 쓰는 기존 서비스와 호환 |
| Jackson3 NDJSON | 신규 Kotlin/JVM 서비스의 행 단위 데이터 교환 |
| GraphML | 다른 그래프 도구와 교환하거나 표준 XML 형식이 필요할 때 |
NDJSON은 데이터 형식이고, Jackson2와 Jackson3는 NDJSON을 읽고 쓰는 직렬화 구현입니다. 현재
bluetape4k-graph에는 두 Jackson 계열 모듈이 구현되어 있습니다.
OkIO는 데이터 형식이 아닙니다. bluetape4k-io와 bluetape4k-okio는 기존 형식 위에 버퍼링, 압축, 압축 해제
크기 제한, 원자적 파일 쓰기와 같은 입출력 기능을 조합합니다. 단일 스트림 형식인 NDJSON과 GraphML에는 고수준 암호화
도우미도 바로 적용할 수 있습니다. CSV는 정점과 간선을 두 파일로 나누는 계약을 사용하므로 고수준 암호화 도우미가
지원하지 않으며, 암호화가 필요하면 저수준 경로 래퍼로 두 파일의 배치를 직접 구성해야 합니다.
공통 계약은 graph-io-core에 있고, 각 형식 모듈은 동기, 가상 스레드, 코루틴 suspend 변형을 제공합니다. 실행
모델은 파일 입출력, 파서, 그래프 저장소 쓰기 비용과 그래프 크기를 함께 고려해 선택해야 합니다.
벤치마크 구현과 실행 방식
섹션 제목: “벤치마크 구현과 실행 방식”이 글의 수치는 2026년 4월 18일에 커밋한 빠른 실행 결과를 기준으로 합니다. 벤치마크 구현은 현재도
BulkGraphIoBenchmark와 BulkGraphIoBenchmarkState에서 관리하지만, 현재 실행 구성은 당시 기록과 다릅니다.
@State(Scope.Benchmark)open class BulkGraphIoBenchmarkState { @Param("small", "medium") var sizeName: String = "small"
lateinit var ops: GraphOperations lateinit var tempDir: Path}벤치마크 상태는 임시 디렉터리를 만들고 메모리 기반 TinkerGraph에 데이터를 넣습니다. small 데이터 세트는
Person 정점 1,000개와 KNOWS 간선 2,000개입니다. 난수 시드를 고정해 같은 테스트 데이터를 반복해서 만들며,
벤치마크 메서드는 그래프를 CSV, NDJSON, GraphML 파일로 내보내거나 파일을 다시 TinkerGraph로 가져옵니다.
@Benchmarkfun csvSyncExport(s: BulkGraphIoBenchmarkState) { val sink = CsvGraphExportSink( GraphExportSink.PathSink(s.tempDir.resolve("v.csv")), GraphExportSink.PathSink(s.tempDir.resolve("e.csv")), ) CsvGraphBulkExporter().exportGraph(sink, s.ops, exportOpts)}커밋된 결과의 실행 환경은 Mac mini Pro M4, 메모리 48GB, Java 25입니다. 이 빠른 실행은 메모리 기반 TinkerGraph와 임시 파일을 사용하므로 별도의 그래프 데이터베이스 컨테이너를 실행하지 않습니다. Neo4j, Memgraph, AGE, FalkorDB 비교 벤치마크와 통합 테스트에서는 Docker와 Testcontainers를 사용합니다.
2026년 4월 18일 기록은 @Fork(0), 워밍업 1회×1초, 측정 2회×1초로 실행했습니다. 회귀 징후를 빠르게 확인하기
위한 결과이며 정밀 성능 자료는 아닙니다. 현재 소스는 @Fork(1), 워밍업 3회×3초, 측정 5회×3초로 강화되어
있습니다. 전체 JMH 실행과 배선 검증용 스모크 테스트도 구분해야 합니다.
# 현재 JMH 전체 실행./gradlew :graph-io-benchmark:benchmark
# CI에서 대표 경로와 보고서 생성만 확인./gradlew :graph-io-benchmark:smokeBenchmarksmokeBenchmark는 smoke 크기의 대표 CSV, Jackson3 OkIO, GraphML OkIO 왕복 경로가 연결되고 보고서가 생성되는지
확인합니다. 이 결과를 형식 간 성능 비교 자료로 해석해서는 안 됩니다.
벤치마크 결과
섹션 제목: “벤치마크 결과”아래 차트는 2026년 4월 18일 빠른 실행에서 작은 그래프 1,000개 정점과 2,000개 간선을 처리한 평균 지연 시간을
요약합니다. JMH 모드는 AverageTime, 단위는 ms/op이며 값이 낮을수록 빠릅니다. 분산까지 비교하려면 현재의 전체
벤치마크 구성으로 다시 실행하고 원시 JSON 결과를 함께 보존해야 합니다.

대표 숫자는 이렇습니다.
| 작업 | 평균 지연 시간(ms/op) |
|---|---|
| CSV 내보내기 | 1.017 |
| Jackson2 NDJSON 내보내기 | 1.194 |
| Jackson3 NDJSON 내보내기 | 1.275 |
| GraphML 내보내기 | 2.582 |
| CSV 가져오기 | 17.854 |
| Jackson2 NDJSON 가져오기 | 18.831 |
| Jackson3 NDJSON 가져오기 | 19.852 |
| GraphML 가져오기 | 21.111 |
같은 기록에서 GraphML 내보내기는 팩터리 재사용과 버퍼 스트림을 적용하기 전 약 413ms, 적용 후 약 2.5ms로
측정되었습니다. 현재 구현도 XMLInputFactory와 XMLOutputFactory를 싱글턴으로 재사용하고 입출력을 버퍼링합니다.
이는 GraphML 형식 자체보다 팩터리 생성과 버퍼링 같은 실행 경로가 결과에 큰 영향을 줄 수 있음을 보여줍니다. 다만 이
차이는 당시 변경 전후 결과이며, 현재 환경의 개선 폭으로 일반화해서는 안 됩니다.
당시 결과에서 Jackson2와 Jackson3의 코루틴 suspend 가져오기는 각각 151.415ms와 155.279ms로 측정되었습니다.
결과 문서는 runBlocking 안에서 Dispatchers.IO 초기화 비용이 함께 측정되었을 가능성을 원인으로 추정합니다. 이는
현재 운영 서비스의 코루틴 성능을 입증하는 결과가 아닙니다. 표와 차트뿐 아니라 벤치마크 구성과 초기화 경계까지 확인해야
하는 이유입니다.
어떻게 쓰면 좋을까
섹션 제목: “어떻게 쓰면 좋을까”테스트 데이터는 CSV나 NDJSON으로 시작할 수 있습니다. 사람이 파일을 열어 정점과 간선을 확인해야 하면 CSV가 적합하고, 서비스 간 교환이나 스냅샷 재생이 목적이면 NDJSON이 자연스럽습니다. GraphML은 그래프 도구와 표준 XML 형식으로 교환해야 할 때 선택합니다.
대용량 파일에서는 데이터 형식보다 입출력 경로의 영향이 커질 수 있습니다. 이때 OkIO 기반 어댑터로 버퍼링, 압축, 원자적 쓰기, 가상 스레드 또는 코루틴 경로를 조합합니다. 데이터 형식과 입출력 기능을 분리하면 CSV에서 NDJSON으로 전환하거나 GraphML 내보내기만 별도 경로로 분리하기 쉽습니다. 암호화는 앞서 설명한 형식별 스트림 계약을 먼저 확인해야 합니다.
다음 글에서는 bluetape4k-workshop의 악용 탐지, 추천, 지식 그래프, 소셜 네트워크 예제를 통해 서비스에 그래프
API를 적용하는 방법을 살펴봅니다.
- graph-io 핵심 모듈 README
- CSV 그래프 I/O
- Jackson2 그래프 I/O
- Jackson3 그래프 I/O
- GraphML 그래프 I/O
- OkIO 그래프 I/O
- BulkGraphIoBenchmark
- BulkGraphIoBenchmarkState
- 그래프 I/O 벤치마크 결과
시리즈
섹션 제목: “시리즈”- Part 1: 그래프 데이터베이스 선택 기준
- Part 2: 핵심 API와 실행 모델
- Part 3: 그래프 입출력과 벤치마크 해석
- Part 4: 워크숍 시나리오와 서비스 통합
- Part 5: 가상 스레드 벤치마크 해석
댓글
GitHub 계정으로 의견을 남기거나 reaction을 남길 수 있습니다.