fd vs find vs ripgrep: I Created 10,000 Files to Settle This Debate
TL;DR: fd is ~2.5x faster than find for filename searches, rg demolishes grep by ~3x for content searches, and find + grep combined lose on every single benchmark I ran. But there's a catch: both fd and rg skip hidden files by default, which can bite you if you're not paying attention. Here are the receipts.
Why I Did This
Every time someone posts a shell one-liner using find on Reddit, there's always that guy in the comments: "jUsT uSe fD, iT's fAsTeR." Then someone else chimes in with "actually ripgrep can do that too."
I got tired of the anecdotes. I wanted numbers. Real ones. On real files. So I fired up WSL, generated 10,900 files across 1,506 directories (~143 MB of mixed content), and ran actual benchmarks with hyperfine. No synthetic microbenchmarks, no "I feel like X is faster" — just cold, hard terminal output.
Methodology
The Test Bed
I created a directory at /tmp/fd-benchmark containing:
| Category | Count | Details |
|---|---|---|
| Plain text files | 2,000 |
file_*.txt — 20 bytes each, contains "test content line N" |
| Binary files | 2,000 |
data_*.bin — 15 bytes each |
| Log files | 1,500 |
match_*.log — contains unique "match_this_test_N" strings |
| Config files | 1,000 | nested_file_*.cfg |
| Nested dir files | 1,000 |
level1_*/level2/level3/deep_*.txt + level1_*/shallow_*.txt
|
| Hidden root files | 1,500 |
.hidden_* + .config_*.yml
|
| Hidden dir files | 500 | .hidden_dir/subdir/deep_hidden_*.txt |
| Git objects | 500 | .git/objects/obj_* |
| Multi-ext source files | 800 |
src_*.{py,js,ts,rs,go,java,rb,php,cpp,h,css,html,json,xml,yaml,md} (50 each) |
| Large binary files | 100 |
large_*.dat — 1 MB each (random data) |
| Total | 10,900 |
$ du -sh .
143M .
$ find . -type f | wc -l
10900
$ find . -type d | wc -l
1506
Tools Tested
| Tool | Version | What It Does |
|---|---|---|
find (GNU) |
4.9.0 | The OG. Ships with every Linux distro. |
fd |
10.2.0 | Rust-based find alternative. Smarter defaults, colored output. |
grep (GNU) |
3.11 | Content search. Also the OG. |
rg (ripgrep) |
15.1.0 | Rust-based grep alternative. Respects .gitignore, crazy fast. |
All benchmarks run with hyperfine --warmup 3 --runs 10 on WSL2 Ubuntu, Intel i7, NVMe SSD.
Benchmark 1: Simple Filename Search (Find All *.txt Files)
This is the most common use case. You want every .txt file in a project, recursively.
What I Ran
# GNU find
find . -name '*.txt'
# fd (extension filter)
fd -e txt
# ripgrep listing all files, piped to grep
rg --files | grep '\.txt$'
Results
Benchmark 1: find . -name '*.txt'
Time (mean ± σ): 94.5 ms ± 17.6 ms [User: 21.1 ms, System: 26.1 ms]
Range (min … max): 74.8 ms … 139.9 ms 10 runs
Benchmark 1: fd -e txt
Time (mean ± σ): 39.9 ms ± 8.2 ms [User: 70.7 ms, System: 134.3 ms]
Range (min … max): 30.8 ms … 55.3 ms 10 runs
Benchmark 1: rg --files | grep '\.txt$'
Time (mean ± σ): 117.6 ms ± 12.8 ms [User: 51.5 ms, System: 106.0 ms]
Range (min … max): 104.8 ms … 144.0 ms 10 runs
| Tool | Mean Time | vs find |
|---|---|---|
fd -e txt |
39.9 ms | 🏆 2.37x faster |
find . -name '*.txt' |
94.5 ms | baseline |
| `rg --files \ | grep` | 117.6 ms |
⚠️ The Hidden Files Gotcha
Here's something the benchmarks don't tell you — fd and find returned different results.
$ find . -name '*.txt' | wc -l
3500
$ fd -e txt | wc -l
3000
fd skipped 500 .txt files. Why? Because those 500 files live inside .hidden_dir/, and fd ignores hidden directories by default. It's not a bug — it's intentional. If you want parity with find, you need the -H flag:
$ fd -H -e txt | wc -l
3500
This is either genius or infuriating depending on whether you knew about it before debugging for 20 minutes. (I was debugging for 20 minutes.)
Winner: fd — but learn the -H flag or it'll silently miss files.
Benchmark 2: Content Search (Find Files Containing "test")
You need to know which files reference a string. Classic grep territory.
What I Ran
# GNU grep (excluding .git manually, suppressing permission errors)
grep -r 'test' . --exclude-dir=.git 2>/dev/null
# ripgrep (auto-excludes .git, hidden dirs, binaries)
rg 'test'
Results
Benchmark 1: grep -r 'test' . --exclude-dir=.git 2>/dev/null
Time (mean ± σ): 302.7 ms ± 50.8 ms [User: 92.8 ms, System: 100.2 ms]
Range (min … max): 228.6 ms … 370.8 ms 10 runs
Benchmark 1: rg 'test'
Time (mean ± σ): 103.2 ms ± 6.8 ms [User: 135.3 ms, System: 177.5 ms]
Range (min … max): 95.3 ms … 116.0 ms 10 runs
| Tool | Mean Time | vs grep |
|---|---|---|
rg 'test' |
103.2 ms | 🏆 2.93x faster |
grep -r 'test' |
302.7 ms | baseline |
And look at that consistency — rg has a standard deviation of 6.8ms vs grep's 50.8ms. rg is not just faster, it's predictably faster.
Both returned exactly 3,500 matches:
$ grep -r 'test' . --exclude-dir=.git 2>/dev/null | wc -l
3500
$ rg 'test' | wc -l
3500
Unlike the filename search test, there's no hidden-file discrepancy here because our "test" string only appears in non-hidden files. But rg is still skipping hidden dirs and .git by default — it just happened to not matter in this case.
Winner: rg — not even close. 3x faster with zero tuning.
Benchmark 3: Finding Hidden Files
Sometimes you need to find all the dotfiles and dotdirs. Let's see who handles this best.
What I Ran
# GNU find — no special flags needed, it sees everything
find . -name '.*' -type f
# fd — needs -H to include hidden stuff, regex anchor for dot-prefix
fd -H -t f '^\.'
# ripgrep — needs --hidden to see dotfiles
rg --hidden --files | grep '^\.'
Results
Benchmark 1: find . -name '.*' -type f
Time (mean ± σ): 104.3 ms ± 20.5 ms [User: 16.0 ms, System: 27.6 ms]
Range (min … max): 79.0 ms … 144.5 ms 10 runs
Benchmark 1: fd -H -t f '^\.'
Time (mean ± σ): 40.5 ms ± 9.2 ms [User: 56.9 ms, System: 106.0 ms]
Range (min … max): 26.6 ms … 57.2 ms 10 runs
Benchmark 1: rg --hidden --files | grep '^\.'
Time (mean ± σ): 118.8 ms ± 9.4 ms [User: 64.4 ms, System: 93.3 ms]
Range (min … max): 106.9 ms … 135.0 ms 10 runs
| Tool | Mean Time | vs find |
|---|---|---|
fd -H -t f '^\.' |
40.5 ms | 🏆 2.58x faster |
find . -name '.*' -type f |
104.3 ms | baseline |
| `rg --hidden --files \ | grep` | 118.8 ms |
All three found exactly 1,500 hidden files (we excluded .git/ objects from this count).
$ find . -name '.*' -type f -not -path './.git/*' | wc -l
1500
$ fd -H -t f '^\.' | wc -l
1500
Winner: fd — once you remember -H.
Benchmark 4: Ignoring .git Directories
This is one of fd and rg's marquee features — they auto-ignore .git, .svn, and anything in .gitignore. With find, you have to do it manually.
What I Ran
# GNU find — manual .git exclusion
find . -not -path './.git/*' -type f | wc -l
# fd — auto-ignores .git AND hidden dirs
fd -t f | wc -l
# ripgrep — auto-ignores .git AND hidden dirs
rg --files | wc -l
Results
Benchmark 1: find . -not -path './.git/*' -type f | wc -l
Time (mean ± σ): 139.2 ms ± 16.5 ms [User: 27.9 ms, System: 37.6 ms]
Range (min … max): 119.4 ms … 173.4 ms 10 runs
Benchmark 1: fd -t f | wc -l
Time (mean ± σ): 85.4 ms ± 10.2 ms [User: 82.9 ms, System: 118.2 ms]
Range (min … max): 69.2 ms … 103.9 ms 10 runs
Benchmark 1: rg --files | wc -l
Time (mean ± σ): 129.0 ms ± 12.2 ms [User: 60.7 ms, System: 106.3 ms]
Range (min … max): 111.4 ms … 151.3 ms 10 runs
| Tool | Mean Time | vs find |
|---|---|---|
| `fd -t f \ | wc -l` | 85.4 ms |
| `rg --files \ | wc -l` | 129.0 ms |
| `find . -not -path './.git/*' \ | wc -l` | 139.2 ms |
But Again: The Counts Don't Match
$ find . -not -path './.git/*' -type f | wc -l
10400
$ fd -t f | wc -l
8400
That's a 2,000-file discrepancy. Here's the breakdown:
- 10,900 total files
- Minus 500
.git/objects = 10,400 (whatfindreports with-not -path) - Minus 2,000 hidden files/dirs = 8,400 (what
fdreports by default)
fd and rg don't just skip .git — they skip all hidden files and directories. This is usually what you want (who searches node_modules/.cache/?), but it's worth knowing.
$ fd -H -t f | wc -l # with hidden files
10900
Winner: fd — but understand what "ignore" actually means.
Bonus Tests
Finding Files by Path Pattern
$ time find . -path '*deep*' -type f | wc -l
1000
real 0m0.088s
$ time fd 'deep' -t f | wc -l
500
real 0m0.094s
Again, fd found half as many because .hidden_dir/subdir/deep_hidden_*.txt (500 files) is in a hidden directory. With -H:
$ fd -H 'deep' -t f | wc -l
1000
Simple File Listing Speed
When neither tool is filtering (just "list everything"), the performance gap narrows:
$ time find . -type f | wc -l
10900
real 0m0.110s
$ time fd -t f | wc -l
8400
real 0m0.116s
At raw directory traversal, they're basically tied. fd's advantage comes from its smarter filtering and parallelism, not from faster syscalls.
The Scorecard
| Test | Winner | Margin |
|---|---|---|
Filename search (*.txt) |
fd |
2.37x over find
|
Content search (test) |
rg |
2.93x over grep
|
| Hidden file search | fd |
2.58x over find
|
| .git exclusion + listing | fd |
1.63x over find
|
| Raw listing (no filter) | Tie | ~identical |
Overall winner: fd + rg as a combo. Replace find with fd, replace grep with rg. You'll never look back.
The Real Talk: When to Use What
Use fd when:
- You're doing any filename-based search (replace
find . -name) - You want smart defaults that skip
.git,node_modules, hidden dirs - You want colored, readable output out of the box
- You're typing into an interactive shell and want the shorter syntax
Use find when:
- You're writing a portable script that needs to run on any Linux/Unix
- You need the insane flexibility of
-execwith complex predicates - You're on a machine where you can't install Rust tooling (embedded, minimal containers)
- You need to search hidden files without remembering a flag
Use rg when:
- You're searching file contents (this is what it was built for)
- You want
.gitignore-aware searching (no more grepping throughnode_modules) - You need speed — it's SIMD-accelerated and uses memory mapping
Use grep when:
- You need POSIX compatibility in scripts
- You're piping from stdin with complex flags (
grep -oPwith Perl regex is still great) -
rgisn't installed and you can't install it
The Pipe Combo Pattern (don't do this)
I see this pattern a lot:
rg --files | grep '\.ts$' | xargs rg 'useState'
This is slow and redundant. Both fd and rg can filter by extension natively:
# Instead of the pipe combo:
fd -e ts -x rg 'useState'
# Or even better:
rg --type ts 'useState'
One command. No pipes. Faster.
What I Learned
fdis genuinely faster for filename searches — not by 10x like some blog posts claim, but a solid 2-2.5x in real-world use. That's worth the install.rgvsgrepisn't even a contest — 3x faster with better defaults. If you take one thing from this article:alias grep=rg.Hidden files will bite you. Both
fdandrgskip them by default. This is documented, but the first time you spend 30 minutes wondering why your.envfile isn't showing up, you'll remember to add-H/--hidden.The ergonomics matter more than the speed. Even if
fdwere the same speed asfind, I'd still use it.fd whateveris just nicer thanfind . -name '*whatever*'. The speed is gravy.hyperfineis a beautiful benchmarking tool. If you're ever debating tool performance, just run the damn benchmark instead of arguing on Hacker News.
Reproduce This Yourself
# Install the tools
cargo install fd-find ripgrep hyperfine
# or: apt install fd-find ripgrep hyperfine
# Clone my setup (approximate recreation):
mkdir -p /tmp/fd-benchmark && cd /tmp/fd-benchmark
for i in $(seq 1 2000); do echo "test content line $i" > "file_${i}.txt"; done
for i in $(seq 1 1000); do echo "hidden content $i" > ".hidden_${i}"; done
mkdir -p .hidden_dir/subdir
for i in $(seq 1 500); do echo "deep hidden $i" > ".hidden_dir/subdir/deep_hidden_${i}.txt"; done
mkdir -p .git/objects
for i in $(seq 1 500); do echo "git object $i" > ".git/objects/obj_${i}"; done
# ... etc. Full generation script in the article source.
# Run the benchmarks
hyperfine -w 3 -r 10 "find . -name '*.txt'" "fd -e txt"
hyperfine -w 3 -r 10 "grep -r 'test' . --exclude-dir=.git" "rg 'test'"
Last updated: May 2026. Tools tested: find 4.9.0, fd 10.2.0, grep 3.11, rg 15.1.0. Environment: WSL2 Ubuntu on NVMe SSD.












