Ripgrep: grep·ag·Git grep 등보다 빠른 검색 도구 (2016년)
(blog.burntsushi.net)- ripgrep(
rg) 은 The Silver Searcher식 코드 검색 편의성과 GNU grep 수준의 원시 성능을 결합한 Rust 기반 명령줄 검색 도구로, Linux·Mac·Windows 바이너리를 제공함 - 25개 벤치마크에서 단일 대형 파일과 대규모 디렉터리 검색 모두
ripgrep보다 성능·정확성 면에서 명확히 앞서는 도구가 없었고, Unicode 지원 비용도 작게 유지됨 .gitignore처리, 숨김·바이너리 파일 기본 제외, 파일 타입 필터, PCRE2 선택 지원, 여러 인코딩과 압축 파일 검색, 전처리 필터까지 포함해 코드 검색 도구의 실사용 범위를 넓힘- Linux 커널 저장소와 OpenSubtitles2016 실험의 차이는 리터럴 최적화, Teddy SIMD 다중 패턴 검색, Aho-Corasick, UTF-8 디코딩 방식, 라인 카운팅,
.gitignore처리 비용에 크게 좌우됨 - 많은 작은 파일을 병렬 검색할 때는 메모리 맵이 느려질 수 있고, 단일 큰 파일에서는 유리할 수 있어
ripgrep은 상황에 따라 중간 버퍼 검색과 메모리 맵 검색을 나눠 사용함
ripgrep이 목표로 한 위치
ripgrep은 코드 검색 도구의 편의성과grep류 도구의 성능을 함께 노린 명령줄 검색 도구임- 비교 대상은
GNU grep,git grep,The Silver Searcher(ag),Universal Code Grep(ucg),The Platinum Searcher(pt),sift임 - 벤치마크가 확인하려 한 핵심은 세 가지임
- 단일 파일과 대규모 디렉터리 검색 모두에서
ripgrep보다 명확히 우위인 도구가 없음 - Unicode 지원을 제대로 제공하면서 큰 성능 비용을 요구하지 않음
- 여러 파일을 한 번에 검색할 때 메모리 맵이 대체로 더 빠르기보다 더 느려질 수 있음
- 단일 파일과 대규모 디렉터리 검색 모두에서
- 작성자는
ripgrep과 기반 정규식 엔진을 만든 사람이며, 벤치마크가 선별되어 편향될 수 있음을 밝힘
기능과 기본 동작
ripgrep의 실행 파일 이름은rg임- 기본 검색은 현재 디렉터리를 재귀적으로 탐색하며
.gitignore를 존중하고, 숨김 파일과 바이너리 파일을 건너뜀 .rgignore도 지원하며,.rgignore패턴은.gitignore보다 우선함-u,-uu,-uuu로 ignore 파일 무시, 숨김 파일 포함, 바이너리 파일 포함 범위를 늘릴 수 있음rg -uuu는grep -a -r과 유사함
- 파일 타입 필터를 지원함
rg -tpy foo: Python 파일만 검색rg -Tjs foo: JavaScript 파일 제외--type-add로 새 파일 타입 규칙 추가 가능
grep의 여러 기능도 제공함- 문맥 출력
- 여러 패턴 검색
- 색상 하이라이트
- 전체 Unicode 지원
- 기본 정규식 엔진은 look-around와 backreference를 지원하지 않지만,
-P로 PCRE2 엔진을 선택하면 해당 기능을 사용할 수 있음 - UTF-16 일부 자동 감지와
-E/--encoding기반 인코딩 지정도 지원함- UTF-16, latin-1, GBK, EUC-JP, Shift_JIS 등이 포함됨
-z/--search-zip으로 gzip, xz, lzma, bzip2, lz4 같은 압축 파일 검색을 지원함- PDF 텍스트 추출, 추가 압축 해제, 복호화, 자동 인코딩 감지 같은 임의 전처리 필터도 지원함
사용하지 않을 이유
- 휴대성과 어디서나 사용 가능함이 최우선이면 표준에 맞고 널리 설치된 grep이 적합함
- 다른 도구에 있는 특정 기능이나 버그에 의존하는 경우
ripgrep이 맞지 않을 수 있음 - 일부 성능 엣지 케이스에서는 다른 도구가 더 잘 동작할 수 있음
- 설치할 수 없거나 플랫폼 지원이 없는 경우도 사용하지 못함
grep류 도구의 동작 구조
- 검색 도구는 크게 세 단계를 거침
- 검색할 파일 수집
- 실제 검색
- 결과 출력
grep류 도구는 큰 파일을 잘 검색해야 하므로 정규식 엔진 성능이 중요함ack류 도구는 재귀 디렉터리 탐색과.gitignore같은 ignore 규칙 적용을 빠르게 처리해야 함ripgrep은 두 접근을 결합하려고 함- 빠른 정규식 엔진
- 병렬 검색
- 검색 대상 필터링
파일 수집과 ignore 처리
ack류 도구에서는 현재 디렉터리에서 어떤 파일을 검색할지 빠르게 결정하는 일이 중요함- 디렉터리 순회 성능은 불필요한
stat호출 수에 영향을 받음 ripgrep은 최소한의 시스템 호출을 목표로 하는 재귀 디렉터리 반복자를 사용함.gitignore처리에는 비용이 있음- 각 디렉터리에서 ignore 파일을 찾아야 함
- ignore 패턴을 컴파일해야 함
- 모든 후보 경로에 패턴을 적용해야 함
- Linux 커널 저장소에는 4,640개 디렉터리와 178개
.gitignore파일이 있었음 ripgrep은.gitignore의 의미를 더 완전하게 지원하려고 하며, 가장 최근에 정의된 매칭 패턴에 우선순위를 둠ucg는.gitignore대신 화이트리스트 기반 glob 규칙을 사용해 빠를 수 있지만, 알지 못하는 확장자의 파일을 놓칠 수 있음
정규식 엔진 차이
- 정규식 엔진은 대체로 두 부류로 나뉨
- 백트래킹 기반: 기능이 풍부하지만 일부 입력에서 지수 시간으로 느려질 수 있음
- 유한 오토마타 기반: 기능은 제한될 수 있지만 검색 텍스트 길이에 대해 선형 시간 보장을 제공함
- 도구별 엔진은 다음과 같음
- GNU grep,
git grep: 자체 유한 오토마타 기반 엔진 ripgrep: Rust regex 라이브러리, 유한 오토마타 기반ag,ucg: PCRE 기반 백트래킹pt,sift: Go regex 라이브러리, 유한 오토마타 기반
- GNU grep,
ag와ucg는 PCRE 사용으로 최악의 백트래킹 동작에 노출될 수 있음- 예시 패턴
(a*)* c는 PCRE 기반 도구에서 문제를 만들 수 있지만, 다른 벤치마크 대상 도구들은 문제 없이 처리함
리터럴 최적화와 SIMD
- 단순 문자열 검색에서는 정규식 엔진보다 리터럴 검색 최적화가 더 중요해질 수 있음
- Boyer-Moore는 고전적인 부분 문자열 검색 알고리듬이며, 후보 위치를 빠르게 찾기 위해
memchr같은 루틴을 활용할 수 있음 memchr구현은 SIMD 명령으로 한 번에 16바이트를 검사하는 경우가 많고, 수 GB/s 처리량을 낼 수 있음- Rust regex 라이브러리는 패턴에서 prefix·suffix 리터럴을 적극적으로 추출함
foo|bar(a|b)c[ab]foo[yz](foo)?bar(foo)*bar(foo){3,6}
- 전체 정규식이 단일 리터럴이나 리터럴 alternation으로 분해되면 핵심 정규식 엔진을 아예 쓰지 않을 수 있음
ripgrep은 라인 단위 결과 출력 특성을 활용해 inner literal도 추출함- 예:
\w+foo\d+에서foo를 먼저 찾아 후보 라인만 정규식으로 검증함
- 예:
- 여러 리터럴 검색에는 GNU grep이 Commentz-Walter 유사 알고리듬을 사용하고, Rust regex는 Aho-Corasick 또는 Teddy SIMD 알고리듬을 사용함
- Teddy는 Intel Hyperscan에서 나온 SIMD 기반 다중 패턴 검색 알고리듬이며,
ripgrep이 GNU grep을 앞서는 핵심 최적화 중 하나임
검색 방식: 라인 단위 검색을 피함
- 순진한 구현은 파일을 한 줄씩 읽고 각 줄에 패턴을 적용하지만, 대부분 검색에서 매치는 드물기 때문에 비효율적임
- 검색 도구는 보통 큰 바이트 버퍼를 한 번에 검색함
- 파일을 메모리 맵으로 매핑
- 파일 전체를 메모리에 읽기
- 고정 크기 중간 버퍼로 점진 검색
ripgrep, GNU grep,git grep은 점진 검색을 지원해 파일과 스트림 모두에 적용 가능함- 점진 검색은 구현이 어려움
- 라인 번호 계산
- 버퍼가 줄 중간에서 끝나는 경우 처리
- 긴 줄 처리
- invert match 처리
- 매치 주변 문맥 출력 처리
ripgrep은 구현 복잡성을 감수하고 점진 검색을 사용하며, 벤치마크에서 많은 작은 파일 검색 시 메모리 맵보다 빠른 결과를 보임
출력과 병렬성
- 병렬 검색에서 각 스레드가 바로 출력하면 서로 다른 파일의 결과가 섞일 수 있음
- 모든 병렬 코드 검색 도구는 검색 결과를 메모리의 중간 버퍼에 쓰고, 출력 단계만 직렬화함
- 이 방식은 검색 스레드가 실제 검색을 병렬로 수행하게 해줌
- 단점은 모든 줄이 매치되는 2GB 파일 같은 경우 메모리 사용량이 커질 수 있다는 점임
ripgrep은stdin이나 단일 파일 검색에서는 중간 버퍼 없이stdout에 직접 씀
벤치마크 방법론
- 벤치마크는 최종 사용자의 문제를 기준으로 나뉨
- 대규모 코드 저장소 검색
- 단일 대형 파일 검색
- 검색 패턴은 단순 리터럴, alternation, 가벼운 정규식에 편향되어 있음
- 도구마다 기본 동작이 달라서 공정 비교를 위해 라인 번호, Unicode,
.gitignore, 화이트리스트 같은 조건을 맞추려 함 - 벤치마크 대상 버전은 다음과 같음
ripgrepv0.1.2- GNU grep v2.25
git grepv2.7.4agcommitcda635, PCRE 8.38ucgcommit487bfb, PCRE 10.21 JITptcommit509368siftcommit2d175c
ack은 당시 다른 도구보다 훨씬 느려 제외됨- 벤치마크 러너는 Python 3.5 이상이 필요한
benchsuite이며,ripgrep저장소에 포함됨 - 각 명령은 측정 전에 3회 warm-up을 실행해 OS page cache에 코퍼스가 올라오도록 함
- 각 명령은 10회 측정하고 평균과 표준편차를 기록함
- 실행 환경은 Amazon EC2
c3.2xlarge, Ubuntu 16.04, Xeon E5-2680 2.8GHz, 메모리 16GB, 80GB SSD임 - 설정 로그, 요약 결과, raw CSV도 공개됨
Linux 커널 코드 검색 결과
- 코드 검색 벤치마크는 빌드된 Linux 커널 저장소 commit
d0acc7에서 실행됨 - 빌드된 커널 저장소를 사용한 이유는 빌드 산출물이 저장소에 남아 검색 결과의 관련성과 성능에 영향을 줄 수 있기 때문임
linux_literal_default에서 단순 리터럴PM_RESUME검색은 각 도구의 기본 동작 차이를 드러냄rg는.gitignore를 존중하고 숨김·바이너리 파일을 건너뜀ag와pt도 유사하지만 라인 수를 셈ucg는.gitignore를 읽지 않고 화이트리스트 기반으로 검색함sift는 기본적으로 거의 모든 것을 검색함git grep은 git index에서 검색 파일 집합을 얻는 이점이 있음
.gitignore존중은 결과의 관련성을 높이지만 성능에는 비용이 될 수 있음linux_literal에서rg (whitelist)는ucg와 거의 비슷한 성능을 보였고,rg (ignore)는git grep과 비슷한 수준을 보임rg (ignore) (mmap)와ag (ignore) (mmap)는 메모리 맵 사용으로 느려졌고, 같은 조건에서rg (ignore)가 훨씬 빨랐음- 로컬 머신에서도 메모리 맵 버전은 더 느렸지만 EC2보다 차이는 줄어듦
Unicode와 대소문자 검색
linux_literal_casei에서pt는-i를 Go regexp의(?i)로 처리하면서 크게 느려짐sift는 패턴과 검색 블록을 소문자로 바꾸는 방식을 써서 덜 느려졌지만, 이 최적화는 ASCII 대소문자만 다뤄 Unicode 대소문자 처리에는 부정확함ripgrep은 대소문자 무시 검색을 가능한 리터럴 조합으로 바꾸고, Teddy로 후보 위치를 빠르게 찾음linux_unicode_word의\wAh검색은 Unicode 인식\w가µAh같은 결과를 잡는지 확인함rg와git grep만 Unicode 토글이 가능했고,ag,pt,sift,ucg는 ASCII 전용\w를 사용함git grep은 Unicode 지원을 켜면 큰 성능 비용을 냈지만,ripgrep은 성능 저하가 거의 없었음ripgrep은 UTF-8 디코딩을 유한 상태 기계에 포함해 별도 디코딩 단계 없이 UTF-8 바이트 문자열에 직접 매칭함
정규식 복잡도별 차이
[A-Z]+_RESUME처럼 리터럴 suffix가 있는 정규식에서는rg와ucg가_RESUME을 이용해 빠르게 후보를 찾음ERR_SYS|PME_TURN_OFF|LINK_REQ_RST|CFG_BME_EVT같은 리터럴 alternation에서는ripgrep이 Teddy를 사용하고, 핵심 정규식 엔진을 아예 쓰지 않을 수 있음- 대소문자 무시 alternation에서도
ripgrep은 대소문자 조합 prefix를 만들어 Teddy로 후보를 찾고, 후보만 전체 정규식으로 검증함 \p{Greek}검색에서는 Rust regex와 Go regex만 해당 Unicode 속성을 지원했으며,rg가pt,sift보다 훨씬 빨랐음\p{Greek}대소문자 무시 검색에서sift는 매치를 보고하지 못했고,pt는 Unicode 대소문자 처리를 올바르게 하지 못했음\w{5}\s+...처럼 리터럴이 없는 패턴에서는 regex 엔진 성능이 직접 드러남rg는 Unicode 지원 상태에서도 빠른 편이었음git grep은 Unicode 지원 시 큰 비용을 냄- Unicode DFA는 ASCII DFA보다 훨씬 큰 NFA 상태 집합을 다루며, 예시 수치는 ASCII 약 250개, Unicode 약 77,000개 NFA 상태임
단일 대형 파일 검색
- 단일 파일 벤치마크는 OpenSubtitles2016 샘플을 사용함
- 영어 샘플은 약 1GB
- 러시아어 샘플은 약 1.6GB
- 이 영역에서는 정규식 엔진 성능과 리터럴 최적화가 더 중요해짐
subtitles_literal에서Sherlock Holmes와Шерлок Холмс검색 모두rg가 가장 빨랐음ripgrep은 리터럴에서 희소한 바이트를 골라memchr에 사용하려고 함- 표준 Boyer-Moore 구현은 보통 마지막 바이트를 후보 검색에 씀
rg는 더 드문 바이트를 골라 SIMD 최적화 루프에서 더 오래 건너뛰려 함
- 러시아어 패턴은 UTF-8에서 많은 문자가
\xD0또는\xD1로 시작해 첫 바이트 검색이 비효율적일 수 있음 rg는 사전 계산된 256바이트 빈도표를 이용해\xD0,\xD1대신 더 드문 바이트를 선호함- 단일 큰 파일에서는 메모리 맵을 한 번만 만들면 되므로
rg의 메모리 맵 검색이rg (no mmap)보다 약 25% 빨랐음
단일 파일에서 Unicode와 alternation
subtitles_literal_casei에서rg는 Unicode 대소문자 무시 검색을 올바르게 처리하면서도 빠름- GNU grep은 Unicode 대소문자 무시 검색에서 큰 비용을 냄
- 러시아어 대소문자 무시 검색에서
grep (ASCII)는-i를 사실상 무시하는 것으로 보이며,ag는 0개 매치를 보고함 subtitles_alternate에서 여러 인물 이름 alternation 검색은rg가 영어와 러시아어 모두 가장 빨랐음- 영어 alternation에서
rg는 GNU grep보다 약 한 자릿수 규모로 빨랐음 subtitles_alternate_casei에서는rg가 이전보다 훨씬 느려졌지만 영어에서는 다른 도구를 앞섰음- 이 경우 Teddy가 감당하기에 리터럴 후보가 많아져
rg는 Teddy 대신 Aho-Corasick으로 전환함 ripgrep은 transition table 기반의 “advanced” Aho-Corasick을 사용해 입력 바이트마다 하나의 전이를 수행함
inner literal과 리터럴 없는 패턴
\w+\s+Holmes\s+\w+같은 패턴은 prefix·suffix 리터럴 최적화를 피하도록 구성되었지만, 내부 리터럴Holmes를 활용할 수 있음ripgrep과 GNU grep은 inner literal 최적화를 수행함ripgrep은 Rust regex의regex-syntax를 활용해 패턴 AST에서 리터럴을 추출함- 러시아어 버전
\w+\s+Холмс\s+\w+에서는 Unicode를 제대로 지원하는 도구만 의미 있게 결과를 낼 수 있었음 - 리터럴이 전혀 없는 긴
\w{5}\s+...패턴에서는rg가 영어에서 가장 빠른 축에 있었고, GNU grep의 Unicode 지원 버전은 영어에서 90초 이상, 러시아어에서 4분 이상 걸려 제외됨 ripgrep은 UTF-8 디코딩을 DFA에 포함하는 방식으로 Unicode 지원을 유지하면서도 성능을 확보함
추가 벤치마크
everything은 Linux 저장소에서.*로 모든 줄을 매치시키는 비현실적 테스트임rg는 22,065,361줄을 1.081초에 보고함ag와pt는 모든 줄을 보고하지 않아 매치 제한이 있는 것으로 보임
nothing은.*에 invert match를 적용해 아무 줄도 보고하지 않는 테스트임rg는 0.302초,git grep은 0.905초를 기록함pt와ucg는 invert search를 지원하지 않음
context는 영어 자막 코퍼스에서Sherlock Holmes주변 2줄 문맥을 출력함rg는 0.612초,sift는 0.717초로 비슷했음ucg는 해당 기능을 지원하지 않음
huge는 9.3GB 영어 자막 전체에서Sherlock Holmes를 검색함rg는 1.786초, GNU grep은 5.119초,sift는 3.047초를 기록함ucg는 라인 카운팅 조건에서 1,543줄만 보고해 잘못된 결과를 냈고, 2GB 초과 파일 검색에서 문제가 생긴 것으로 의심됨
결론
ripgrep은 Linux 커널 저장소 검색에서 모든 벤치마크를 항상 이기지는 않았지만, 성능과 정확성에서 다른 도구가 명확히 우위라고 말하기 어려웠음git grep은 일부 단순 사례에서 몇 밀리초 앞설 수 있었지만, 패턴이 복잡해지거나 Unicode가 요구되면ripgrep이 크게 앞서는 경우가 있었음ripgrep의 코드 검색 성능에는 다음 요소가 기여함- 최소
stat호출을 목표로 한 빠른 디렉터리 순회 RegexSet을 이용한.gitignoreglob 매칭- Chase-Lev work stealing queue를 통한 작업 분배
- 많은 작은 파일 검색에서 메모리 맵을 쓰지 않는 선택
- 빠른 정규식 엔진
- 최소
- 단일 파일 검색에서는
ripgrep이 모든 주요 벤치마크에서 가장 빠르거나 큰 차이로 앞섬 - 단일 파일 성능에는 희소 바이트 기반
memchr, Teddy SIMD, Aho-Corasick, UTF-8 디코딩 내장 DFA가 영향을 줌 - Unicode 기능을 요구하는 벤치마크에서
rg, GNU grep,git grep만 의미 있는 지원을 보였고, GNU grep과git grep은 대체로 큰 성능 비용을 냄 - 메모리 맵은 Linux x86_64 기준으로 많은 작은 파일 병렬 검색에서는 불리했고, 단일 큰 파일 검색에서는 유리했으며, VM 환경에서는 추가 페널티가 있을 수 있음
댓글과 토론
Hacker News 의견들
-
확실히 빠르고, fzf 조합을 계속 추천하게 됨
ripgrep로 먼저 찾은 뒤 결과 파일+텍스트 위에서 퍼지 검색을 얹고,bat으로 문맥을 보여주는 PowerShell 함수로 쓰고 있음
여러 저장소가 섞인 프로젝트에서 “어딘가에 있는 건 아는데 정확한 위치나 이름을 모를 때” 아주 빠르게 좁혀갈 수 있음
이 방식은 https://github.com/junegunn/fzf/blob/master/ADVANCED.md에서 나온 것이고, 전부 쓰지 않더라도 아이디어를 얻기 위해 훑어볼 만함- 한 단계 더 나아가 ripgrep-all(rga) 을
fzf와 통합하는 걸 추천함
텍스트 파일뿐 아니라 PDF, zip 같은 여러 파일 형식까지 퍼지 검색할 수 있음
자세한 내용은 https://github.com/phiresky/ripgrep-all/wiki/fzf-Integration에 있음 - 이걸 bash 버전으로도 작성했음
rg결과를fzf로 고르고, 선택한 파일과 줄 번호를 파싱해서$EDITOR +"${linenumber}" "$file"로 여는 방식임 - Vim에서 fzf+rg가 없으면 거의 망가진 느낌임
전기 그라인더 대신 손으로 커피를 가는 것 같음 fzf를 쓰면 Git에 추가할 파일을 많이 고르면서 일부는 건너뛸 수 있음
gitconfig의[alias]에fza = "!git ls-files -m -o --exclude-standard | fzf -m --print0 | xargs -0 git add"를 넣으면git fza로 수정됐거나 아직 추가되지 않은 파일 목록이 뜨고, 스페이스로 항목을 토글하며 다음으로 이동함
이 별칭과 fzf+fd가 작업 흐름 일부를 꽤 빠르게 해줌
macOS에서 zsh 설정에 넣을 것들을 정리한 가이드도 있음: https://gist.github.com/aclarknexient/0ffcb98aa262c585c49d4b...- 나도
ripgrep을 거의 같은 방식으로 씀
수백 개 저장소가 있는 코드베이스에서 파일이나 프로젝트를 좁혀가는 출발점으로 쓰고, 그다음에 더 파고듦
- 한 단계 더 나아가 ripgrep-all(rga) 을
-
Emacs에서
ripgrep을 project.el과 dumb-jump 패키지로 쓰고 있음
가장 인기 있는 방식은 아닐 수 있지만 전체 경험이 꽤 만족스러움
package-install로dumb-jump를 설치하고(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)만 설정하면 됨
Python 프로젝트에서M-.또는C-u M-.로 식별자 정의를 찾으면dumb-jump가 현재 프로젝트와 파일 형식에 맞춰rg명령을 실행하고 결과를 Xref 버퍼에 표시함
ag도 지원하고,ag나rg가 없으면grep으로 돌아가는데 홈 디렉터리 전체를 찾을 때는 예상대로 느릴 수 있음- Emacs 기본 제공 project.el만으로도
ripgrep을 꽤 쉽게 쓸 수 있음
외부 패키지가 꼭 필요한 건 아니고, 큰 디렉터리에서 느린grep대신 쓰려면(setq xref-search-program 'ripgrep)를 설정하면 됨
그러면C-x p g foo RET같은 프로젝트 검색이 현재 프로젝트에서rg -i --null -nH --no-heading --no-messages -g '!*/' -e foo형태로 실행됨
결과가 Xref 버퍼에 떠서n,p,RET,C-o같은 키로 다음/이전 매치 이동, 소스 점프, 분할 창 표시가 편함 ripgrep작성자로서 보기에, 그 정규식은 직접 실행해보진 않았지만 --pcre2 플래그를 빼도 될 것 같음
두 번째와 세 번째\b단언도 빼도 될 듯하고, 첫 번째는 필요할 수 있음- Deadgrep은
ripgrep을 쓰고evil-collection바인딩도 있어서 만족스럽게 쓸 수 있음: https://github.com/Wilfred/deadgrep - 이 방식도 좋지만, 여러 프로젝트를 한 번에 찾거나 프로젝트 안의 하위 폴더만 찾고 싶을 때는 여전히 rg.el을 씀
그럴 때 원래는rgrep을 썼을 상황임
- Emacs 기본 제공 project.el만으로도
-
흥미로운 점은 VS Code 검색도 이제 Node.js 래퍼를 통해
ripgrep으로 동작한다는 것임
https://www.npmjs.com/package/@vscode/ripgrep- VS Code는 요청하거나 설치할 수 있지만
ripgrep은 못 까는 환경이라면 이게 아주 좋음
VS 설치 경로 안에서rg바이너리를 찾을 수 있음. 적어도 내 Windows 직장 환경에서는 가능했음 - VS Code가 Electron 앱인데 검색이 왜 그렇게 빠른지 늘 궁금했는데, 이제 이유를 알겠음
- 새 기능은 아니고, VS Code에는 7년 전부터 들어가 있었음
- VS Code는 요청하거나 설치할 수 있지만
-
ripgrep을 2년 정도 써왔고 이제 없어서는 안 되는 도구가 됨
grep에서 갈아탄 주된 이유는 사용 편의성이었음
기본적으로.gitignore규칙을 존중하고 숨김 파일/디렉터리와 바이너리 파일을 건너뛰기 때문에rg search_term directory가 대응되는grep명령보다 훨씬 낫고, 속도 향상은 덤임
매치가 너무 길어서 터미널이 난장판이 될 때는-M 1000처럼 -M 옵션을 자주 씀-M은 정말 훌륭함
보고 싶지 않은 minified 파일 결과를 무시할 때 특히 편하고,-g *.cs처럼 -g 옵션으로 특정 확장자 파일만 검색하는 것도 좋음
독립 실행 가능한 이식성 있는 바이너리라는 점도 유용해서, 새 머신에서 작업할 때 실행 파일을 넣고grep별칭을rg로 잡아두면 손에 익은 대로grep을 쳐도rg가 실행됨
-
2023년에도 여전히 맞는 말일 수 있지만, 문제는 병렬화된
grep대체 도구들, 예를 들어ripgrep이나ag가 기존grep보다 너무 빨라서 서로 간의 작은 속도 차이가 구분 기준이 되기 어렵다는 것임
90만 줄 코드베이스에서 Emacs 안에서ag를 쓰는데, 16코어 Ryzen Threadripper 2950X에서는 사실상 즉시 끝남
1초 미만을 “조금 더 1초 미만”으로 줄일 필요를 못 느낌
새로운grep류 도구의 핵심 속성은 속도가 아니고, 다른 방식으로 평가하고 비교해야 함- 2016년에는 속도가 확실히 핵심 속성이었다고 봄
ag에는 꽤 큰 성능 절벽이 있고, 블로그 글에서도 볼 수 있음
다만 작업 부하는 사람마다 달라서 어떤 경우에는 성능 차이가 중요하지 않을 수 있음
90만 줄은 그리 큰 편이 아니고, 단순 질의라면 순진하지 않은grep류 도구는 대부분 아주 빨리 처리함
다른 비교 기준으로 보면ag는 거의 생명 유지 상태이고, Debian에서 제거될 뻔했다가 누군가 살린 것으로 보임: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=999962
블로그 글은 유니코드 지원도 비교하는데,ag는 사실상 유니코드 지원이 없음. 모든 사람에게 중요하진 않겠지만 비성능 비교 기준으로는 충분함 - 내 경험으로는 이런 도구들은 전부 심하게 입출력 병목에 묶여 있음
검색 시간은 파일이 디스크에서 로드되는 시간만큼 걸리고, 그 뒤의 차이는 의미 있기 어려움
파일이 캐시에 있으면 검색 시간보다 파일 시스템을 이동하고 명령을 쓰는 시간이 더 지배적이라 역시 성능 차이가 의미 있기 힘듦
- 2016년에는 속도가 확실히 핵심 속성이었다고 봄
-
제목에 (2016) 이 필요함
이건 원래 발표 글이지 새 정보가 아님- 관련 토론은 다음과 같음
“Ripgrep – A new command line search tool” https://news.ycombinator.com/item?id=12564442 (740 points | Sept 23, 2016 | 209 comments) — 속도 관련 토론도 있음
“Ripgrep is faster (2016)” https://news.ycombinator.com/item?id=17941319 (98 points | Sept 8, 2018 | 40 comments)
- 관련 토론은 다음과 같음
-
qgrep보다는 빠르지 않음
둘의 동작 방식은 크게 다르고,qgrep은re2기반이지만 속도는 인덱스가 있기 때문에 나옴
큰 파일 저장소에서는 매번 모든 파일을 훑기보다qgrep과 인덱스를 쓰는 편이 더 말이 되는데 사람들이 왜qgrep선택지를 잊는지 궁금함
다만 UTF-8에서 여러 줄 매칭이 필요하면ripgrep은 다른 PCRE2 라이브러리로 돌아가야 해서 그렇게 빠르지 않다고 봄ripgrep작성자로서,qgrep이 인덱싱을 쓰기 때문에 인덱싱하지 않는 도구보다 유리한 건 맞음
대신 인덱스를 설정하고 유지해야 하므로 UX가 “그냥 검색 실행”만큼 단순하진 않음
사람들이qgrep을 안 쓰는 이유는 “내게는 grep도 충분히 빠르다”는 이유로ripgrep을 안 쓰는 것과 비슷함
작은 검색 대상에서는ripgrep과grep, 또는qgrep과ripgrep의 속도 차이를 체감하지 못하는 경우가 많음
Linux 커널 검색을ripgrep이 100ms 안에 끝낸다면, 표준적인 대화형 사용에서 인덱싱 도구로 갈아탈 만큼 불편할지는 상황에 따라 다르지만 대개는 아닐 것임
ripgrep에 인덱싱을 추가하는 아이디어를 생각해본 적은 있음: https://github.com/BurntSushi/ripgrep/issues/1497
그리고 여러 줄 검색은 PCRE2를 요구하지 않음. 기본 정규식 엔진도 유니코드 지원이 있고, PCRE2 없이 빌드해도 여러 줄 검색 지원은 유지됨
-
ripgrep에서 ugrep으로 갈아탄 뒤 돌아보지 않게 됨
속도도 비슷하지만 퍼지 매칭이 있고, 코드 리뷰에 쓸 만한 TUI도 있으며, PDF나 압축 파일 안도 검색할 수 있음
선택적으로 Google 검색 문법을 쓸 수 있는 것도 편함
https://ugrep.com- 나는
ripgrep열성 팬이지만, 최근ripgrep에 없는 기능인 zip 압축 내부 검색 때문에ugrep을 찾게 됨
디스크에 압축을 풀지 않고 검색할 수 있음
수백만 개의 작은 텍스트 파일로 된 압축 말뭉치를 다루는데, 전체를 파일 시스템에 풀 필요가 없어져서 좋음. 어떤 파일 시스템은 이 규모에서 힘들어함
두 도구 모두 고맙고, 각각의 작성자에게 감사함 grep에서 Google 검색 문법을 쓰기 시작하면 결과 대부분이 뭔가를 팔려고 할까 봐 겁남- “ugrep vs ripgrep” 글을 가볍게 찾다가,
ugrep과ripgrep작성자들이 Reddit에서 몇 년간 다툰 듯한 글들을 봤음
예를 들면 https://www.reddit.com/r/programming/comments/120wqvr/ripgre...
그냥 오픈소스 도구 얘기인데 좀 이상하게 느껴짐 - TUI가 결과를
fzf로 넘기는 것보다 나은지 궁금함
내게는fzf의 설정 가능성과 유연성을 이기기 어려워 보임 - 이걸 알려줘서 고마움
킬러 기능은 기존 grep 명령줄 옵션 호환성인 것 같음
완전히 새로운 옵션 집합을 배울 필요가 없는 게 꽤 좋음
- 나는
-
왜
grep은 대체되거나 개선되지 않는지 궁금함
이 주제도 이제 좀 오래된 것 같음- 설명할 수 있는 이유는 많음
관성, 호환성, 변화에 대한 저항, 혁신가의 딜레마 같은 것들임. 부정적으로 말하는 건 아니고, 나에게도 전부 적용됨
호환성에 관해서는 FAQ를 보면 됨: https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#pos... - 지금 앉아 있는 40년 된 의자를 Razer UltraSeat XR3000-A로 바꾸지 않는 이유와 비슷함
편하고, 주변 작업 환경에 잘 맞고, 굳이 바꾸고 전부 다시 맞출 이유가 없음
Razer 같은 의자가 근처에 이미 있어서 옷을 맡고 있다는 점까지만 비유가 이어짐 - Unix를 설계한 누군가가 일부 시스템 기능을 핵심 OS 기능이면서 동시에 사람이 쓰는 도구로 만들었고, 그 결과 수십 년 뒤에는 “
xyz라는 프로그램이 반드시 있어야 하고, 이 인자를 받아 정확히 이렇게 동작해야 한다” 같은 이상한 결과가 생김 - 이미
ripgrep같은 여러 대체 도구를 쓸 수 있음
grep명령 자체를 다른 유틸리티로 바꾸자는 거라면, 얻는 가치에 비해 깨질 것이 너무 많아 보임
더 빠른grep을 원하는 사람은 다른 도구를 쓰고, 기존grep을 쓰는 사람은 계속 쓰면 되니 이미 이상적인 상태에 가까움 grep은 모든 종류의 파일에서 텍스트를 찾는 범용 도구이고, UNIX 표준에 박혀 있음
일부 프로그래머는 소스 코드 검색에 쓰지만, 다른 사람들은 소스 코드와 무관한 텍스트 검색이나 스크립트에서 쓰며, 절대 크래시하지 않기를 기대함
반면ripgrep은 주로 소스 코드 저장소 검색을 위해 설계된 전문적이고 의견이 강한 도구임
범용 텍스트 검색을 더 빠르게 만들 여지는 많지 않음.mmap()을 쓰면 잘린 파일에서 크래시 위험이 있고, 정규식 표현력을 줄이면 더 빨라질 수 있으며, 모든 locale과 문자셋 지원을 버리고 UTF-8/UTF-16만 하드코딩할 수도 있지만 그래서는 안 됨
- 설명할 수 있는 이유는 많음
-
Portage에서 찾아보니 PDF와 doc 같은 다른 문서까지 다루는 버전도 있는 것 같음
https://github.com/phiresky/ripgrep-all