DEV Community

Mike Chen
Mike Chen

Posted on

fd vs find vs ripgrep: I Created 10,000 Files to Settle This Debate

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
Enter fullscreen mode Exit fullscreen mode

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$'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

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 '^\.'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

That's a 2,000-file discrepancy. Here's the breakdown:

  • 10,900 total files
  • Minus 500 .git/ objects = 10,400 (what find reports with -not -path)
  • Minus 2,000 hidden files/dirs = 8,400 (what fd reports 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 -exec with 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 through node_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 -oP with Perl regex is still great)
  • rg isn'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'
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

One command. No pipes. Faster.


What I Learned

  1. fd is 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.

  2. rg vs grep isn't even a contest — 3x faster with better defaults. If you take one thing from this article: alias grep=rg.

  3. Hidden files will bite you. Both fd and rg skip them by default. This is documented, but the first time you spend 30 minutes wondering why your .env file isn't showing up, you'll remember to add -H / --hidden.

  4. The ergonomics matter more than the speed. Even if fd were the same speed as find, I'd still use it. fd whatever is just nicer than find . -name '*whatever*'. The speed is gravy.

  5. hyperfine is 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'"
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)