도구스개발

외부 정렬(디스크 병합) 패스 계산기

메모리에 다 들어가지 않는 파일을 정렬할 때 필요한 런 개수·병합 패스·총 디스크 입출력을 냅니다. 병합 차수를 올리면 패스가 주는 대신 버퍼가 작아지는 맞바꿈을 표로 나란히 보여 줍니다.

GB
GB

정렬에 실제로 내줄 수 있는 양입니다. 버퍼도 여기서 나눠 씁니다.

한 번에 몇 개의 런을 열어 합칠지입니다.

KB
MB/s

0으로 두면 시간을 계산하지 않습니다. 읽기와 쓰기를 합친 실효 처리량으로 넣으세요.

런 100개 · 16-way 병합

3패스 · 입출력 600 GB

파일 크기의 6배를 읽고 씁니다. 처리량 200MB/s면 약 51.2분 걸립니다.

런 하나의 길이1 GB
초기 런 개수100
병합 패스2
총 패스 (런 만들기 포함)3
총 입출력600 GB
스트림 하나당 버퍼60.24 MB
버퍼가 담는 블록60.2
예상 시간51.2분

패스별 진행

패스전 런 수후 런 수입출력
런 만들기100200 GB
1차 병합1007200 GB
2차 병합71200 GB

어느 패스든 파일 전체를 한 번 읽고 한 번 씁니다. 그래서 총 입출력이 2 × 파일 × 패스 수로 딱 떨어지고, 외부 정렬을 빠르게 하는 일은 곧 「패스 수를 줄이는 일」이 됩니다.

병합 차수를 바꿔 보면

k병합 패스총 입출력스트림당 버퍼
271,600 GB341.33 MB
441,000 GB204.8 MB
83800 GB113.78 MB
162600 GB60.24 MB
322600 GB31.03 MB
642600 GB15.75 MB
1281400 GB7.94 MB
2561400 GB3.98 MB
10241400 GB1,023 KB

k를 올리면 패스가 줄지만 버퍼가 그만큼 작아집니다. 병합 중에는 입력 런 k개와 출력 하나가 메모리를 나눠 쓰기 때문입니다. 빨간 줄은 버퍼가 블록 8개도 담지 못해 순차 읽기가 깨지는 구간입니다. 실무의 k는 「패스를 가장 줄이는 값」이 아니라 「버퍼가 충분한 한도 안에서 가장 큰 값」으로 정합니다.

치환 선택은 런을 만들 때 힙을 유지하며 「새로 읽은 값이 방금 내보낸 값보다 크면 이번 런에 끼워 넣는」 방식입니다. 무작위 입력에서 런 길이의 기댓값이 메모리의 두 배가 되어 런 수가 절반이 됩니다. 런이 절반이면 병합 패스가 하나 줄어드는 경우가 생기고, 그때는 파일 크기의 두 배만큼 입출력이 통째로 사라집니다. 대신 런을 만드는 단계 자체는 힙 유지 비용 때문에 느려집니다.

사용 방법

  1. 1정렬할 파일 크기와 쓸 수 있는 메모리를 넣습니다.
  2. 2병합 차수 k를 정합니다. 아래 칩으로 흔한 값을 넣어 볼 수 있습니다.
  3. 3디스크 블록 크기를 넣습니다. 버퍼가 블록 몇 개를 담는지 판단하는 데 씁니다.
  4. 4「병합 차수를 바꿔 보면」 표에서 패스와 버퍼가 어떻게 맞바뀌는지 봅니다. 빨간 줄은 버퍼가 너무 작아진 구간입니다.
  5. 5치환 선택을 켜서 런 수가 절반이 될 때 패스가 줄어드는지 확인합니다.

자주 묻는 질문

메모리에 한꺼번에 올릴 수 없는 데이터를 디스크를 오가며 정렬하는 방법입니다. 먼저 메모리에 들어가는 만큼 읽어 정렬해 되쓰고(런 만들기), 그렇게 만든 정렬된 조각들을 여러 개씩 열어 합치기를 되풀이합니다. 100GB 파일을 1GB 메모리로 정렬하는 것 같은 일이 여기 해당합니다.

런 개수 R = ⌈파일/메모리⌉이고, 병합 차수 k로 합칠 때마다 런이 1/k로 줄어드므로 병합 패스는 ⌈log_k R⌉입니다. 여기에 런을 만드는 첫 패스를 더하면 총 패스가 됩니다. 어느 패스든 파일 전체를 한 번 읽고 한 번 쓰므로, 총 입출력은 2 × 파일 크기 × 총 패스로 딱 떨어집니다.

버퍼가 작아져서 안 됩니다. 병합하는 동안 입력 런 k개와 출력 하나가 메모리를 나눠 쓰므로 스트림 하나당 버퍼가 메모리/(k+1)입니다. k를 키워 버퍼가 디스크 블록 몇 개 크기로 쪼그라들면 한 번에 읽는 양이 줄어 순차 읽기가 임의 접근으로 바뀌고, 회전 디스크에서 이 차이는 두 자릿수입니다. 그래서 실무의 k는 패스를 가장 줄이는 값이 아니라 버퍼가 충분한 한도 안에서 가장 큰 값으로 정합니다.

런을 길게 만드는 기법입니다. 메모리를 채우고 정렬해 통째로 비우는 대신, 힙을 유지하면서 가장 작은 값을 계속 내보내되 새로 읽은 값이 방금 내보낸 값보다 크면 이번 런에 끼워 넣습니다. 무작위 입력에서 런 길이의 기댓값이 메모리의 두 배가 되는 것이 알려져 있어 런 수가 절반이 됩니다. 런이 절반이면 병합 패스가 하나 줄어드는 경우가 생기고, 그때는 파일 크기의 두 배만큼 입출력이 통째로 사라집니다.

패스 수와 총 입출력을 세는 계산은 같지만 버퍼 크기의 무게가 다릅니다. SSD는 탐색 시간이 없다시피 해서 버퍼가 작아질 때의 손해가 회전 디스크만큼 크지 않고, 그래서 k를 더 크게 잡을 수 있습니다. 다만 큐 깊이와 쓰기 증폭이라는 다른 제약이 생기므로 무한정 올릴 수 있는 것은 아닙니다.

런을 모두 한꺼번에 열면 됩니다. 즉 k가 런 개수 이상이어야 하고, 이 계산기가 그 값을 「한 패스로 끝내려면 필요한 k」로 보여 줍니다. 그때 총 입출력은 파일 크기의 네 배(런 만들기에서 2배, 병합에서 2배)로 최소가 됩니다. 다만 그만한 k에서 버퍼가 남아나는지를 먼저 확인해야 합니다.

전송되지 않습니다. 모든 계산이 브라우저 안에서 이뤄지고, 입력값은 이 기기에만 남습니다.

알아두면 좋은 점

  • 패스 수를 ⌈log_k R⌉로 «계산»하지 않고 런 수를 실제로 k로 나눠 가며 셉니다. 부동소수점 로그는 R이 k의 거듭제곱일 때 경계에서 어긋나기 때문입니다(Math.log(1000)/Math.log(10)이 3이 아니라 2.9999999999999996으로 나오는 문제). k가 2·3·5·10·16이고 런이 정확히 그 거듭제곱인 경우를 5제곱까지 확인했습니다.
  • 100GB를 1GB 메모리로 10-way 병합하면 런 100개·병합 2패스·총 3패스·입출력 600GB가 나옵니다. 2-way면 7패스이고 런 수가 100 → 50 → 25 → 13 → 7 → 4 → 2 → 1로 줄어듭니다. 손으로 셀 수 있는 값이라 그대로 테스트에 박아 두었습니다.
  • 총 입출력이 언제나 2 × 파일 × 총 패스와 같은지, 패스 표의 마지막 런 수가 반드시 1인지, 패스마다 런 수가 반드시 줄어드는지를 여러 조합으로 확인합니다.
  • 버퍼 경고 기준은 스트림 하나가 블록 8개를 담지 못할 때입니다. 정해진 규격이 아니라 순차 읽기를 유지하려면 이쯤은 필요하다는 어림이며, 실제 값은 저장장치와 파일시스템에 따라 다릅니다.
  • 치환 선택의 런 길이 2배는 무작위 입력에서의 기댓값입니다. 입력이 이미 어느 정도 정렬돼 있으면 훨씬 길어지고, 역순으로 정렬돼 있으면 2배에 못 미칩니다.
  • 이 계산기는 입출력 양만 셉니다. 비교 비용·CPU 시간·압축·병렬 디스크는 다루지 않고, 예상 시간도 총 입출력을 처리량으로 나눈 값일 뿐입니다.

함께 보면 좋은 도구

마지막 검증: 2026년 9월 2일 · 결과는 참고용 추정치입니다.