콘텐츠로 이동

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

로봇 작업자들이 CSV, NDJSON, GraphML, OkIO 파일을 그래프 입출력 컨베이어로 옮기는 3D 작업대 일러스트
대량 입출력은 데이터 이전뿐 아니라 테스트 재현성과 운영 분석에도 필요합니다.

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

GraphOperations에서 graph-io-core, 데이터 형식, 입출력 데코레이터, 운영 활용, 벤치마크 검증 단계로 이어지는 그래프 입출력 파이프라인
형식 어댑터는 그래프 서비스 코드와 파일 형식을 분리하고, OkIO는 입출력 경로를 조합합니다.

현재 graph-io에서 구현된 데이터 형식은 CSV, Jackson2 NDJSON, Jackson3 NDJSON, GraphML입니다.

형식적합한 용도
CSV사람이 직접 확인하고 수정하기 쉬운 소규모 테스트 데이터
Jackson2 NDJSONJackson2를 쓰는 기존 서비스와 호환
Jackson3 NDJSON신규 Kotlin/JVM 서비스의 행 단위 데이터 교환
GraphML다른 그래프 도구와 교환하거나 표준 XML 형식이 필요할 때

NDJSON은 데이터 형식이고, Jackson2와 Jackson3는 NDJSON을 읽고 쓰는 직렬화 구현입니다. 현재 bluetape4k-graph에는 두 Jackson 계열 모듈이 구현되어 있습니다.

OkIO는 데이터 형식이 아닙니다. bluetape4k-iobluetape4k-okio는 기존 형식 위에 버퍼링, 압축, 압축 해제 크기 제한, 원자적 파일 쓰기와 같은 입출력 기능을 조합합니다. 단일 스트림 형식인 NDJSON과 GraphML에는 고수준 암호화 도우미도 바로 적용할 수 있습니다. CSV는 정점과 간선을 두 파일로 나누는 계약을 사용하므로 고수준 암호화 도우미가 지원하지 않으며, 암호화가 필요하면 저수준 경로 래퍼로 두 파일의 배치를 직접 구성해야 합니다.

공통 계약은 graph-io-core에 있고, 각 형식 모듈은 동기, 가상 스레드, 코루틴 suspend 변형을 제공합니다. 실행 모델은 파일 입출력, 파서, 그래프 저장소 쓰기 비용과 그래프 크기를 함께 고려해 선택해야 합니다.

이 글의 수치는 2026년 4월 18일에 커밋한 빠른 실행 결과를 기준으로 합니다. 벤치마크 구현은 현재도 BulkGraphIoBenchmarkBulkGraphIoBenchmarkState에서 관리하지만, 현재 실행 구성은 당시 기록과 다릅니다.

@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로 가져옵니다.

@Benchmark
fun 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 실행과 배선 검증용 스모크 테스트도 구분해야 합니다.

Terminal window
# 현재 JMH 전체 실행
./gradlew :graph-io-benchmark:benchmark
# CI에서 대표 경로와 보고서 생성만 확인
./gradlew :graph-io-benchmark:smokeBenchmark

smokeBenchmarksmoke 크기의 대표 CSV, Jackson3 OkIO, GraphML OkIO 왕복 경로가 연결되고 보고서가 생성되는지 확인합니다. 이 결과를 형식 간 성능 비교 자료로 해석해서는 안 됩니다.

아래 차트는 2026년 4월 18일 빠른 실행에서 작은 그래프 1,000개 정점과 2,000개 간선을 처리한 평균 지연 시간을 요약합니다. JMH 모드는 AverageTime, 단위는 ms/op이며 값이 낮을수록 빠릅니다. 분산까지 비교하려면 현재의 전체 벤치마크 구성으로 다시 실행하고 원시 JSON 결과를 함께 보존해야 합니다.

작업당 밀리초로 나타낸 그래프 입출력 단기 실행 벤치마크 차트
GraphML 팩터리 재사용처럼 구현 세부가 결과를 크게 바꿀 수 있으므로 수치와 측정 조건을 함께 봐야 합니다.

대표 숫자는 이렇습니다.

작업평균 지연 시간(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로 측정되었습니다. 현재 구현도 XMLInputFactoryXMLOutputFactory를 싱글턴으로 재사용하고 입출력을 버퍼링합니다. 이는 GraphML 형식 자체보다 팩터리 생성과 버퍼링 같은 실행 경로가 결과에 큰 영향을 줄 수 있음을 보여줍니다. 다만 이 차이는 당시 변경 전후 결과이며, 현재 환경의 개선 폭으로 일반화해서는 안 됩니다.

당시 결과에서 Jackson2와 Jackson3의 코루틴 suspend 가져오기는 각각 151.415ms와 155.279ms로 측정되었습니다. 결과 문서는 runBlocking 안에서 Dispatchers.IO 초기화 비용이 함께 측정되었을 가능성을 원인으로 추정합니다. 이는 현재 운영 서비스의 코루틴 성능을 입증하는 결과가 아닙니다. 표와 차트뿐 아니라 벤치마크 구성과 초기화 경계까지 확인해야 하는 이유입니다.

테스트 데이터는 CSV나 NDJSON으로 시작할 수 있습니다. 사람이 파일을 열어 정점과 간선을 확인해야 하면 CSV가 적합하고, 서비스 간 교환이나 스냅샷 재생이 목적이면 NDJSON이 자연스럽습니다. GraphML은 그래프 도구와 표준 XML 형식으로 교환해야 할 때 선택합니다.

대용량 파일에서는 데이터 형식보다 입출력 경로의 영향이 커질 수 있습니다. 이때 OkIO 기반 어댑터로 버퍼링, 압축, 원자적 쓰기, 가상 스레드 또는 코루틴 경로를 조합합니다. 데이터 형식과 입출력 기능을 분리하면 CSV에서 NDJSON으로 전환하거나 GraphML 내보내기만 별도 경로로 분리하기 쉽습니다. 암호화는 앞서 설명한 형식별 스트림 계약을 먼저 확인해야 합니다.

다음 글에서는 bluetape4k-workshop의 악용 탐지, 추천, 지식 그래프, 소셜 네트워크 예제를 통해 서비스에 그래프 API를 적용하는 방법을 살펴봅니다.

댓글

GitHub 계정으로 의견을 남기거나 reaction을 남길 수 있습니다.