lsr: io_uring을 사용한 ls

1 week ago 8

  • lsr은 io_uring 기반의 IO를 활용하여 기존 ls 대안들보다 훨씬 더 빠른 성능적은 시스템 콜 수를 달성함
  • 벤치마크 결과로, 더 많은 파일이 있을수록 lsr의 시스템 콜 수와 실행 시간 모두 타 프로그램보다 확연한 우위임
  • 디렉토리 내용을 읽고 처리할 때 stat 호출까지 io_uring으로 처리하여 성능과 효율을 크게 개선함
  • Zig의 StackFallbackAllocator 사용 및 라이브러리 동적 링크 생략으로 파일 크기와 syscall 수 감소를 달성함
  • 정렬 작업이 전체 작업에서 큰 비중을 차지하며, locale 지원은 없지만 정적 링크로 GNU ls 대비 파일 크기도 작음

소개 및 의의

lsr 프로젝트는 일반 ls 명령어의 대체제로서 io_uring을 활용하는 빠른 디렉토리 리스팅 도구임
기존의 ls, eza, lsd, uutils ls와 비교해, 실행 속도시스템 콜 사용량에서 탁월한 성능을 보임
우리 io 라이브러리(ourio)로 최대한 많은 IO를 직접 수행하는 실험적 접근임
벤치마크를 통해 lsr이 대규모 파일환경에서도 빠른 성능과 품질을 증명함

벤치마크 결과

벤치마크는 hyperfine으로 n개의 일반 파일이 있는 디렉토리에서 측정하였음

  • lsr -al: 10–10,000개 파일 기준, 기존 ls/대체제 대비 월등히 짧은 실행 시간을 기록함
  • 예: 10,000개 파일에서 lsr이 22.1ms, ls가 38.0ms, eza가 40.2ms, lsd가 153.4ms, uutils ls가 89.6ms

시스템 콜 집계는 strace -c로 진행

  • lsr -al: 최소 20회(n=10)부터 최대 848회(n=10,000)로 매우 낮은 콜 수 유지
  • ls는 최대 30,396회(n=10,000), 나머지 대체제도 수천~십만 회 수준임
  • 동일 조건에서 lsr이 최소 10배 이상 적은 syscall 수로 최고의 효율 달성

내부 동작 방식

lsr이 디렉토리 내용을 출력하는 과정은 크게 세 단계임

  1. 인자 파싱
  2. 데이터 수집: 주요 IO 과정이 여기에서 발생하며, 필요한 데이터는 대부분 io_uring을 통해 처리
    • 타겟 디렉토리 오픈, 시간/유저/그룹 정보 파일 오픈 & 읽기, 모든 stat 호출 등
    • 작업을 io_uring으로 처리하여 stat syscall을 배치(batch) 처리, syscall 수를 대폭 줄임
    • strace 결과 uutils ls와 비교해도 범주의 차이가 나타남
  3. 데이터 출력

최적화 및 추가 기술 요소

  • Zig StackFallbackAllocator를 이용해 초기 1MB 메모리를 선할당하고, 초과시만 다른 할당자 사용
    • mmap syscall도 최소화, syscall 수 추가 감소
  • libc 동적 링크 없이 오직 statically linked 방식 적용
    • GNU ls 대비 lsr의 ReleaseSmall 빌드 사이즈가 138.7KB vs 79.3KB로 더 작음
  • locale 지원은 불가

추가 관찰 및 생각

  • lsd는 각 파일마다 clock_gettime을 반복 호출해, syscall 수가 매우 높은 현상 발생
  • 정렬 작업이 전체 처리 시간의 30% 차지, uutils ls는 syscall 수는 적지만 정렬 병목으로 속도가 낮음
  • io_uring 직접 활용이 syscall 절감을 극명하게 보여주며, 서버 같이 IO 집중 서비스에 큰 개선 효과 기대 가능
  • tangled.sh에서 PR 관리 및 개선 작업이 이루어지므로, 아이콘 등의 기능 개선 이슈 제안을 환영함

결론

  • lsr은 빠른 속도, 적은 시스템 콜, 간결한 크기를 동시에 달성하는 실험적 ls 대체제임
  • 대용량 파일 환경이나 고성능 IO가 중요한 시스템에 매우 적합함
  • locale 미지원 등 일부 기능 한계가 있지만, 실무와 벤치마크 모두에서 혁신적인 결과를 보임

Read Entire Article