중복 제거
중복 제거 (Deduplication)
Xet 지원 리포지토리는 content-defined chunking (CDC)을 이용해 바이트 수준에서 중복을 제거해요(약 64KB 데이터, "청크(chunk)"라고도 불러요). 각 청크는 실제 파일 내용에 따라 청크 경계를 결정하는 롤링 해시(rolling hash)로 식별되며, 파일 어디에서든 삽입·삭제에 탄력적이에요. Xet 인지 클라이언트를 사용해 Xet 지원 리포지토리에 파일을 업로드하면 내용이 이 가변 크기 청크들로 분해돼요. 청크 후 Xet 스토리지에 이미 없는 새 청크만 유지되고, 나머지는 모두 버려져요.
청크 수준 중복 제거 덕분에 소규모 수정만 업로드되므로 대규모 모델·데이터 리포지토리의 전송이 훨씬 빨라져요.
출처: 문서
본문
[!TIP] 청크 수준 중복 제거 덕분에 서버 측 복사가 즉시 이뤄져요: 리포지토리·버킷 간
hf buckets cp는 데이터 재업로드 없이 내용 해시만 마이그레이션해요. 리포지토리·버킷 간 파일 복사를 참고해요.
Content-Defined Chunking이 작동하는 방식
content-defined chunking을 이해하려면 파일을 긴 텍스트 구절로 상상해 보세요. 시스템은 롤링 해시 — 바이트 위를 미끄러지듯 움직이는 작은 수학 함수 — 를 사용해 데이터를 스캔해요. 해시가 특별한 패턴에 도달할 때마다 그 위치에 청크 경계가 놓여요. 경계가 (고정된 위치가 아니라) 내용 자체에 의해 결정되기 때문에, 주변 내용이 바뀌어도 동일한 데이터 영역은 항상 같은 청크를 만들어내요.
왜 고정 크기 청크가 아닐까?
파일 중간에 소량의 데이터를 삽입하면 어떤 일이 일어나는지 생각해 보세요. 고정 크기 청크에서는 삽입 지점 뒤의 모든 청크 경계가 이동해서, 대부분의 데이터가 변하지 않았는데도 아래쪽의 모든 청크가 무효화돼요:
Original file, fixed 6-byte chunks:
|The qu|ick br|own fo|x jump|s over| the l|azy do|g |
chunk1 chunk2 chunk3 chunk4 chunk5 chunk6 chunk7 chunk8
Insert "very " before "lazy":
|The qu|ick br|own fo|x jump|s over| the v|ery la|zy dog|
chunk1 chunk2 chunk3 chunk4 chunk5 chunk6 chunk7 chunk8
~~~~~~ ~~~~~~ ~~~~~~
3 chunks changed!
5바이트만 삽입했는데 8개 중 3개 청크가 바뀌었어요. 수정 뒤의 모든 경계가 5자리만큼 이동했기 때문이에요. 64KB 청크 크기의 실제 파일에서 소소한 수정 하나가 수백 메가바이트의 청크를 무효화할 수 있어요.
Content-Defined Chunking은 경계를 안정적으로 유지해요
CDC에서는 경계가 — 고정 간격이 아닌 — 내용이 패턴과 일치하는 곳에 놓여요. 이는 삽입이 발생한 청크에만 영향이 미친다는 뜻이에요. 앞뒤의 청크는 동일하게 남아요:
Original file, content-defined chunks (boundaries marked by "|"):
|The quick |brown fox |jumps over |the lazy dog|
chunk 1 chunk 2 chunk 3 chunk 4
Insert "very " before "lazy":
|The quick |brown fox |jumps over |the very lazy dog|
chunk 1 chunk 2 chunk 3 chunk 4'
(same) (same) (same) (changed)
4개 중 1개 청크만 바뀌었어요 — 수정이 포함된 그 청크만요. 나머지 셋은 바이트 단위로 동일해서 중복 제거돼요. 이것이 CDC가 버전 관리 데이터에 효과적인 이유예요: 모델 체크포인트를 업데이트하거나 데이터셋에 행을 추가할 때 수정된 부분만 업로드·저장하면 돼요.
청크에서 스토리지로
전체 중복 제거 파이프라인은 다음과 같이 동작해요:
flowchart LR
A["File"] --> B["Content-Defined\nChunking"]
B --> C{"Chunk already\nstored?"}
C -- "Yes (duplicate)" --> D["Skip upload\n(reuse existing)"]
C -- "No (new)" --> E["Group into\n64 MB blocks"]
E --> F["Upload to\nXet Storage"]
파일이 청크되면 각 청크의 해시를 이미 저장된 것과 대조해요. 이는 여러 수준에서 일어나요: 먼저 현재 업로드 세션에서 이미 본 청크와, 그다음 이전에 업로드한 메타데이터의 로컬 캐시와, 마지막으로 전역 중복 제거 쿼리를 통해 Xet 스토리지 전체의 청크 하위 집합과 대조해요. 중복 청크는 완전히 건너뛰고, 새 청크는 64MB 블록으로 묶어 업로드해요. 각 블록은 내용 주소 저장소(CAS)에 해시를 키로 한 번만 저장돼요.
실제 스토리지 절감
Hub의 현재 권장사항은 파일을 200GB로 제한하는 것이에요. 64KB 청크 크기에서 20GB 파일은 312,500개의 청크를 가지며, 그중 상당수는 버전마다 변하지 않아요. Git LFS는 파일이 바뀌었는지만 알아차리고 그 리비전 전체를 저장하도록 설계됐어요. Xet 백엔드는 청크 수준에서 중복을 제거해서 파일에서 수정된 내용만(몇 KB나 MB밖에 안 될 수 있어요) 저장하고, 리포지토리 간 공유 블록을 안전하게 중복 제거해요. Model·Dataset 리포지토리에 있는 대형 바이너리 파일의 경우 이는 파일 전송 시간을 크게 개선해줘요.
자세한 내용은 From Files to Chunks와 From Chunks to Blocks 블로그 포스트, 또는 Hugging Face에 인수되기 전 XetHub의 출발점이 된 Low et al.의 Git is for Data 논문을 참고해요.
더 알아보기 (Learn more)
- Xet은 콘텐츠 정의 청킹으로 바이트 수준 중복 제거를 해서 변경분만 업로드해요.
- CDC는 수정된 청크만 무효화하므로 버전 관리 데이터의 반복 갱신이 빠르고 경제적이에요.
- Xet 개요와 Git LFS와의 호환성 문서를 함께 참고해 보세요.