Git 2.56's last-modified command is fast on one directory and slow on scattered files
Git 2.56의 last-modified 명령은 한 디렉터리에서는 빠르고 흩어진 파일에서는 느립니다
Git 2.56의 실험적 git last-modified는 같은 디렉터리의 파일 40개를 조회할 때 파일별 git log보다 3.5배 빨랐지만, 저장소 곳곳의 파일에서는 더 느렸습니다. 디렉터리와 그 하위 경로를 함께 넘길 때 인자 순서에 따라 결과가 누락되는 문제도 확인했습니다.
- 주제
에디터 노트
같은 디렉터리에서는 40개 파일 조회가 3.5배 빨랐지만, 흩어진 경로 40개에서는 오히려 0.6초 느렸습니다. 따라서 이 결과가 뒷받침하는 기준은 흩어진 경로를 금지하는 것이 아니라, 해당 사례에서 성능 이점이 없다는 정도입니다. 100개 실험의 격차는 더 커졌지만 측정은 git/git 저장소와 한 가상 머신에서 이뤄졌습니다. 하위 트리 가지치기가 성능 차이를 만든다는 설명은 댓글에서도 나왔고 작성자도 동의했지만 코드로 확인하지 않았습니다. 반면 부모 경로와 자식 경로의 순서에 따라 행이 사라지는 동작은 실제 출력으로 확인했습니다. 성능 설명은 경로 형태가 달라질 때 생기는 현상으로 남지만, 경로 순서 버그는 파일 브라우저가 바로 재현할 수 있는 구체적인 문제입니다.
AI 요약
Git 2.52.0에 추가된 git last-modified는 여러 경로의 마지막 변경 커밋을 한 번의 이력 탐색으로 찾는 명령입니다. 파일마다 git log -1 -- <경로>를 실행하면 매번 이력을 다시 탐색하므로, 여러 파일을 한꺼번에 조회할 때 비용을 줄이려는 목적입니다. 명령은 Git 2.56.0에서도 실험적 기능으로 표시되어 있습니다.
작성자는 git/git 저장소를 blob 없이 복제하고 Git 2.56.0을 직접 빌드해 비교했습니다. 저장소에는 커밋 82,542개와 추적 파일 4,857개가 있었고, 실험은 4코어 가상 머신에서 진행했습니다. 파일 내용은 필요하지 않아 전체 커밋과 트리 이력만 가져왔습니다.
한 디렉터리에 모인 경로에서는 빠릅니다
저장소 최상위 디렉터리에 있는 파일 40개를 조회한 결과, git last-modified는 가장 빠른 실행에서 861ms, 파일별 git log 반복 실행은 2,981ms가 걸렸습니다. 약 3.5배 빠른 결과입니다. 작성자는 파일 5개의 커밋 해시를 git log -1 결과와 직접 대조해 일치하는 것을 확인했습니다.
같은 디렉터리의 파일은 이력을 함께 바꾼 커밋이 겹치는 경우가 많습니다. 명령이 이력을 한 번 훑으면서 여러 경로에 대한 답을 찾는 방식은 이런 중복이 있을 때 유리합니다.
저장소 곳곳에 흩어진 경로에서는 이점이 사라집니다
같은 저장소와 기기에서 무작위로 고른 파일 40개를 조회하자 git last-modified는 6.402초, 파일별 git log 반복은 5.793초가 걸렸습니다. 파일 수를 50개로 늘렸을 때 각각 7.48초와 6.59초였고, 100개에서는 25.7초와 12.2초였습니다. 500개를 조회하는 실험은 CPU를 4분 넘게 사용한 뒤 중단했습니다.
작성자는 서로 이력이 덜 겹치는 경로를 처리할 때 명령의 경로별 관리 비용이 커지는 것으로 보인다고 설명합니다. 다만 내부 코드를 분석해 원인을 확인하지는 않았습니다. 댓글에서 제기된 하위 트리 가지치기 설명도 작성자가 그럴듯하다고 동의했지만, 코드로 검증한 결과는 아닙니다. 따라서 실측 결과는 특정 저장소와 환경에서 확인한 성능 차이이며, 모든 저장소의 원인을 확정한 것은 아닙니다.
경로 하나만 조회할 때도 git log -1이 더 빨랐습니다. COPYING을 대상으로 한 5회 실험에서 git last-modified의 최솟값은 684ms였고 최대 1,582ms까지 걸렸습니다. git log -1은 최솟값 431ms, 최댓값 744ms였습니다. 여러 경로에 이력 탐색을 나눠 쓰는 이점이 없는 단일 경로에서는 git log -1을 쓰는 편이 낫다는 결과입니다.
경로 인자 순서에 따라 결과가 달라집니다
기본값인 --max-depth=0은 하위 트리로 내려가지 않는다고 매뉴얼에 설명되어 있습니다. 작성자는 contrib 디렉터리와 contrib/completion 하위 경로를 각각 단독으로 넘기면 경로마다 한 줄씩 결과가 나오는 것을 확인했습니다. 두 경로를 함께 넘기면 결과가 달라졌습니다. 부모인 contrib를 먼저 쓰자 디렉터리의 집계 행이 사라지고 즉시 하위 디렉터리 18개의 결과가 출력됐습니다. 순서를 뒤집어 하위 경로를 먼저 쓰면 contrib 한 줄로 다시 축약됐습니다.
작성자는 이 동작이 인자 순서에 따라 조용히 결과를 바꾸며, 경로 목록을 사용자 선택에 따라 만드는 파일 브라우저에서 잘못된 목록으로 이어질 수 있다고 지적합니다. 디렉터리와 그 자식 경로를 한 호출에 함께 넘기지 말고 디렉터리별로 따로 호출하라고 제안합니다. 댓글에서는 일반 정렬이 부모 경로를 먼저 놓아 오히려 문제가 재현될 수 있다는 답변도 나왔습니다.
그 밖의 확인 결과와 실행 조건
Git 2.54 변경 기록에 나온 트리 객체 인자 오류는 확인 결과 명확한 오류 메시지를 출력했습니다. 메모리는 git last-modified의 50개 경로 조회와 파일별 git log 중 가장 메모리를 많이 쓴 프로세스가 모두 약 190MB로 비슷했습니다. 작성자는 두 경우 모두 커밋 그래프와 팩 인덱스 로딩이 메모리 사용량의 대부분을 차지한다고 설명합니다. 4개의 디렉터리 조회를 동시에 실행했을 때는 4코어 환경에서 순차 실행보다 짧은 벽시계 시간이 나왔습니다.
Git 2.56.0을 직접 빌드하는 과정에서는 curl/curl.h가 없어 실패했습니다. 원격 전송 기능을 사용하지 않더라도 기본 빌드가 curl 기반 코드를 컴파일하려 했기 때문입니다. NO_CURL=1과 NO_EXPAT=1을 지정해 빌드하면 글의 실험에 쓸 수 있었다고 설명합니다.
dev.to 반응
- @vishisht_choudhary_a12d08 — 흩어진 경로 실험이 가장 유용합니다. 벤치마크는 보통 디렉터리 하나를 조회하는 잘되는 사례에서 끝나기 때문입니다. 모든 경로에 공통 부모가 있으면 하위 트리 전체를 건너뛸 수 있지만, 흩어진 경로에서는 커밋마다 저장소 대부분의 트리를 비교해야 하는 것 아닐까요? 부모 경로를 먼저 썼을 때 사라지는 행이 경로를 정렬하면 없어지는지도 궁금합니다. 명령이 아직 실험적일 때 Git 메일링 리스트에 제보할 만합니다.
- @alexgeorgiev17 — 하위 트리를 건너뛴다는 추측은 그럴듯해 보입니다. 공통 부모가 있으면 바뀌지 않은 트리를 통째로 건너뛰지만, 경로가 흩어지면 거의 모든 커밋에서 관심 경로가 바뀌었는지 확인해야 하는 것 같습니다. 50개에서 100개로 늘 때 시간이 두 배가 아니라 세 배가 된 결과도 그 설명과 맞지만, 코드를 확인하지는 않았습니다. 일반 정렬은 오히려 문제를 더 자주 일으킵니다. contrib가 contrib/completion보다 먼저 정렬되므로 문제가 나는 순서가 됩니다. 역순 정렬은 시험하지 않았고, 파일 브라우저가 인자 순서에 따라 올바른 결과를 내야 하는 구조도 원하지 않습니다. 디렉터리와 그 자식을 함께 넘기지 않는 편이 낫습니다. 메일링 리스트에도 실제로 제보해야겠습니다.
- @sgaggjhkjh — 경로 순서 버그를 찾아낸 점이 좋습니다. 잘되는 사례가 아니라 문제가 나는 사례를 시험해야 드러나는 동작입니다. 디렉터리 목록에는 쓰고 흩어진 경로에는 쓰지 않는다는 실용적인 기준도 다음에 이 명령을 쓸 때 기억하겠습니다.
- @alexgeorgiev17 — 고맙습니다. 다만 흩어진 경로를 쓰지 말라는 기준은 수치보다 강한 표현입니다. 6.4초와 5.8초이므로 정확히는 큰 이점이 없다는 뜻입니다. 실제로 이득이 있는 경우는 단일 디렉터리입니다. 어떤 방식으로 쓰든 디렉터리와 그 자식을 같은 호출에 넣지 마세요. 한 행이 조용히 사라지는 경우입니다.
- @sgaggjhkjh — 정정해 주셔서 감사합니다. 6.4초 대 5.8초라는 수치와 단일 디렉터리가 가장 적합하다는 설명 덕분에 기준이 더 분명해졌습니다.
- @efe_genc — 흩어진 파일 결과가 가장 유용합니다. 느려지는 원인이 경로가 닿는 디렉터리 수인지 가장 깊은 경로의 깊이인지 확인했나요? 디렉터리 수가 원인이라면 호출자가 경로를 부모별로 묶어 실행해 파일 브라우저의 빠른 경우를 유지할 수 있습니다. 파일 5개의 해시를 git log -1과 대조한 점도 좋습니다. 벤치마크 글에서 자주 빠지는 단계입니다. 부모와 자식 경로의 순서 버그는 인자 순서만으로 재현되고 간단히 확인할 수 있으니 Git 메일링 리스트에 제보할 만합니다.
원문: dev.to / 번역·요약: Trawling