fastfind is designed to be a modern, feature-rich alternative to traditional tools like find and fd. It prioritizes flexibility and capability while still aiming for strong performance.
| Command | Time | Files Found | Notes |
|---|---|---|---|
ff "*" / |
~0.53s | ~1.09M | Fastest ff mode (single-thread) |
ff "*" / -H |
~0.71s | ~1.39M | Includes hidden files |
fd . / |
~0.27s | ~1.09M | Extremely optimized baseline |
fd . / -H |
~0.36s | ~1.09M | Hidden files |
fdis faster (about 2× for raw traversal)fffinds slightly more files when using-H, due to differences in how hidden or system paths like/procand/sysare handledffsingle-thread mode is the fastest mode. Parallel (-j) adds overhead for simple workloads
fd is an extremely well-optimized tool written in Rust with:
- A highly tuned filesystem walker (
ignorecrate) - Very low allocation overhead
- Efficient use of OS directory APIs
- Years of refinement and real-world usage
For pure filename traversal, fd is genuinely hard to beat.
fastfind is not trying to replace fd. It is trying to extend what file search tools can do.
Where fd focuses on speed and simplicity, ff focuses on:
- Doing more in one command
- Reducing the need for pipelines
- Adding smarter, higher-level capabilities
fastfind aims to combine multiple tools into one:
findis powerful but complexfdis fast and simplefzfis great for filtering but not a crawler
fastfind sits between them:
- one command
- more features
- still reasonably fast
fastfind holds its own while offering features that go beyond fd:
-
Built-in content search (
--contains) no grep pipeline needed -
Natural language queries
"python files modified this week""code files larger than 100kb" -
Fuzzy matching no need for
fzfin simple cases -
Semantic code search
--function,--class,--symbol -
Interactive mode built-in selection UI with
:execand:rmcommands -
Index-based search (optional) fast incremental updates, lock-free reads
-
Single static binary minimal dependencies
fastfind is built around a simple idea:
Do the least amount of work possible per file.
This led to key optimizations:
- universal
*fast path - matcher bypass (
matchAll) - pattern match before
stat - early continue for non-matches
- buffer reuse (avoid allocations)
- buffered output (reduce syscalls)
- local variable hoisting
The result:
- about 2× speedup from initial versions
- competitive performance for a feature-rich tool
- strong single-thread efficiency
Parallel mode exists, but:
- it is not always faster
- for simple traversal, it is often slower
Why:
- filesystem traversal is I/O-bound
- thread coordination adds overhead
- per-entry work is too small to benefit
Use -j when:
- content search (
--contains) - complex filtering
- regex-heavy workloads
- CPU-heavy per-file processing
- simple
*or glob scans - full filesystem listing
- lightweight queries
| Scenario | Recommendation |
|---|---|
| Pure speed (file listing) | fd |
| Rich filtering or features | ff |
| Content search (simple) | ff |
| Content search (max speed) | fd + grep |
| Fuzzy search without pipelines | ff |
| Interactive pipelines | fzf |
fastfind is not trying to beat fd at its own game.
Instead, the goal is:
A single, powerful search tool that reduces the need for chaining multiple tools together.
The long-term vision:
- close more of the performance gap where possible
- keep the hot path efficient and predictable
- expand higher-level search capabilities
- make advanced queries easier and more natural
- stay fast enough to feel instant in real workflows
In short:
- fd = speed and simplicity
- ff = capability, flexibility, and still fast
- fd is faster, and that is expected
- ff is more capable, and that is intentional
fastfind is about doing more with one command, without giving up too much performance.
Below is more information on fastfind. Read below to learn how to install, use, and/or compile this software.
Cross-platform, single binary, zero runtime dependencies (written in Nim).
⚠ fastfind is still early in development. Expect occasional bugs or behavioral changes.
Below is software supported, and software used in this project. click on any to be redirected to respected website.
fastfind/ff is a fast file finder with:
- Glob/regex/fixed/fuzzy matching
- Natural language queries (BETA)
- Size/time/content filters
- Git-aware filtering
- Interactive mode
- Optional index-based search
- Semantic symbol search
Official aliases (recommended):
ffffindqfsfind
(qf = quickfind, sfind = speed find)
Install latest published binary with
curl -fsSL https://raw.githubusercontent.com/RobertFlexx/fastfind/main/install.sh | bashFor some BSDs or Linux Distributions:
curl -fsSL https://raw.githubusercontent.com/RobertFlexx/fastfind/main/install.sh | shparu -S fastfind-binyay -S fastfind-binUpdate to the latest version:
curl -fsSL https://raw.githubusercontent.com/RobertFlexx/fastfind/main/update.sh | bash-
The update script checks your current version and only downloads if there's a new release.
-
DISCLAIMER: binaries for other OSes, or musl libc Linux distributions may not ALWAYS be available. Primarily glibc Linux with architecture x86_64.
-
HINT: there are dynamic binaries for: FreeBSD[amd64], OpenBSD[amd64], NetBSD[amd64], Darwin[arm64], Linux[glibc, amd64] (may not be immediately available for newly released versions)
git clone https://github.com/RobertFlexx/fastfind
cd fastfind
nim c -d:danger -d:release --mm:orc --threads:on -d:lto --opt:speed \
--passC:-O3 --passC:-march=native --passC:-flto --passL:-flto --passL:-s \
-o:bin/fastfind src/ff.nimNushell:
git clone https://github.com/RobertFlexx/fastfind
cd fastfind
nim c '-d:danger' '-d:release' '--mm:orc' '--threads:on' '-d:lto' '--opt:speed' '--passC:-O3' '--passC:-march=native' '--passC:-flto' '--passL:-flto' '--passL:-s' '-o:bin/fastfind' src/ff.nim| Flag | Purpose | Recommended |
|---|---|---|
-d:danger |
Remove debug checks, runtime validation | ✅ Required for speed |
-d:release |
Enable compiler optimizations | ✅ Required |
--mm:orc |
Memory manager (orc = best balance, arc = low memory) | Use orc |
--threads:on |
Enable multi-threading support | ✅ Required |
-d:lto |
Enable link-time optimization | ✅ Required |
--opt:speed |
Optimize for speed not size | ✅ Required |
--passC:-O3 |
C compiler optimization level 3 | ✅ Required |
--passC:-march=native |
Optimize for this CPU | ✅ Required |
--passC:-flto |
C compiler LTO | ✅ Required |
--passL:-flto |
Linker LTO | ✅ Required |
--passL:-s |
Strip symbols (smaller binary) | Optional |
For distribution, very optimized build (generic binary):
nim c -d:danger -d:release --mm:orc --threads:on -d:lto --opt:speed \
--passC:-O3 --passC:-march=x86-64 --passC:-flto --passL:-flto --passL:-s \
-o:bin/fastfind src/ff.nimNushell:
nim c '-d:danger' '-d:release' '--mm:orc' '--threads:on' '-d:lto' '--opt:speed' '--passC:-O3' '--passC:-march=x86-64' '--passC:-flto' '--passL:-flto' '--passL:-s' '-o:bin/fastfind' src/ff.nimDebug build (for testing):
nim c --threads:on -o:bin/fastfind_debug src/ff.nimNushell:
nim c '--threads:on' '-o:bin/fastfind_debug' src/ff.nimProduction build (balanced):
nim c -d:danger -d:release --mm:arc --threads:on --opt:speed \
-o:bin/fastfind src/ff.nimNushell:
nim c '-d:danger' '-d:release' '--mm:arc' '--threads:on' '--opt:speed' '-o:bin/fastfind' src/ff.nimsudo cp bin/fastfind /usr/local/bin/ffIf you see this error when running ff:
could not load: libpcre.so
First, check which library your binary actually needs:
ldd "$(command -v ff)" | rg -i pcreThen install the matching runtime package for your distro:
| OS/Distro | Package Name | Install Command |
|---|---|---|
| Ubuntu/Debian | libpcre3 | sudo apt install libpcre3 |
| Fedora/RHEL/CentOS (current repos) | pcre2 | sudo dnf install pcre2 |
| Arch Linux | pcre (legacy libpcre.so.*) |
sudo pacman -S pcre |
| openSUSE | libpcre1 | sudo zypper install libpcre1 |
| Alpine Linux | pcre | doas apk add pcre |
| macOS | pcre (via brew) | brew install pcre |
| FreeBSD | pcre | sudo pkg install pcre |
| OpenBSD | pcre | doas pkg_add pcre |
If you still see an error for a specific SONAME (for example libpcre.so.3), query which package provides it:
sudo dnf provides '*/libpcre.so*'On Fedora/RHEL 10+, legacy PCRE1 (libpcre.so.*) is no longer in default repos. If your binary was built against PCRE1, use a static release binary or rebuild against currently available libraries.
On Arch, pcre is available but deprecated upstream; new builds should prefer PCRE2.
Tip: Using static builds from the release page avoids this dependency issue entirely.
If you encounter this error on RHEL 10, EL10, or modern Fedora-like systems:
could not load: libpcre.so(.3|.1|)
Root Cause: The binary depends on legacy PCRE1 (libpcre.so.1 / libpcre.so.3). RHEL 10+ and similar distributions no longer provide PCRE1 in default repositories. Installing pcre2 does NOT fix the issue because it is ABI-incompatible.
Workaround with Homebrew (Linuxbrew):
-
Install PCRE:
brew install pcre
-
Run with the library path:
LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib ff
Permanent Fix:
-
Option A: Add to your shell config (
~/.bashrc,~/.zshrc, etc.):export LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib:$LD_LIBRARY_PATH
-
Option B (recommended): Create a wrapper script:
sudo mv /usr/local/bin/ff /usr/local/bin/ff.real sudo tee /usr/local/bin/ff >/dev/null <<'EOF' #!/usr/bin/env bash export LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib:${LD_LIBRARY_PATH:-} exec /usr/local/bin/ff.real "$@" EOF sudo chmod +x /usr/local/bin/ff
Verification:
LD_LIBRARY_PATH=/home/linuxbrew/.linuxbrew/lib ffIf ff runs successfully, the issue is resolved.
Note for developers: Rebuild without PCRE1 dependency or use static builds. Consider migrating away from runtime
dlopenof PCRE1.
Both ff and fd handle full system traversal well. Here's what to expect:
$ time ff "*" / | wc # ~1.09M files (0.58s)
$ time fd . / | wc # ~1.09M files (0.26s)
Key points:
- fd is ~2x faster for pure traversal (highly optimized Rust implementation)
- ff finds comparable file counts (uses similar directory traversal)
- ff with
-Hfinds more files (~1.39M) due to slightly different hidden file handling
For best performance, use explicit glob patterns:
ff "*" / # Explicit glob (fastest for ff)
ff --glob "*" / # Same, more explicitNote: ff's single-thread mode is actually faster than parallel mode (-j) for simple traversal. Use default (no -j) for best results.
# Find Nim files
ff "*.nim" src/
# Fuzzy match
ff --fuzzy config src/
# Content search
ff "*.py" --contains TODO
# Natural language query
ff "python files containing TODO"
# Interactive mode
ff --interactivefastfind can interpret natural language queries and translate them into filters automatically.
ff "python files"
ff "rust code"
ff "config files"
ff "log files"ff "files modified this week"
ff "files modified 2 days ago"
ff "files modified in the last hour"
ff "files older than 30 days"
ff "recent files"ff "large files"
ff "small files"
ff "empty files"
ff "files larger than 10mb"
ff "files between 1mb and 10mb"ff "python code files modified this week"
ff "rust files larger than 100kb"
ff "config files modified today"
ff "large log files modified yesterday"
ff "image files modified this month"ff "python files"
ff "javascript files"
ff "rust code"
ff "go files"
ff "shell scripts"
ff "docker files"
ff "terraform files"ff "python files containing TODO"
ff "config files containing password"
ff "log files containing error"for more documentation, use
man ff(or whatever alias you're using) in the terminal.
find is extremely powerful, but syntax is heavy and platform behavior differs.
fd is excellent for speed and simplicity, but intentionally smaller in scope.
fzf is a fuzzy selector, not a filesystem crawler by itself.
fastfind sits between these: one command with richer features, while keeping command shape compact.
fastfind aims to combine the strengths of several tools:
find: extremely powerful but complexfd: simple and fast but limited scopefzf: great interactive filtering but requires input pipelines
fastfind provides:
- a single consistent command
- built-in fuzzy and content search
- interactive mode
- semantic code queries
- optional indexed search
| Capability | ff |
find |
fd |
fzf |
|---|---|---|---|---|
| Recursive file discovery | Built-in | Built-in | Built-in | Input-driven (needs producer) |
| Glob/regex/literal modes | Built-in switches | Primarily -name/-regex forms |
Built-in regex/glob modes | Fuzzy/text filtering over input |
| Fuzzy matching | Built-in (--fuzzy) |
No | No | Core strength |
| Natural language queries | Built-in (BETA) | No | No | No |
| Size/time filters | Built-in (--size, --changed, etc.) |
Built-in (-size, -mtime, etc.) |
Built-in (--size, --changed-within) |
Via upstream command only |
| Content filtering | Built-in (--contains) |
Via -exec grep/pipeline |
Via -X grep/pipeline |
Via upstream command only |
| Git-aware file filters | Built-in (--git-*) |
No | Partial (--no-ignore-vcs, etc.) |
No |
| Interactive picker | Built-in (--interactive, --select) |
No | No | Core strength |
| Index mode | Built-in (--use-index) |
No | No | No |
| Semantic symbol search | Built-in (--function, --class, --symbol) |
No | No | No |
| Task | ff |
find |
fd |
fzf |
|---|---|---|---|---|
Find all *.nim files |
ff "*.nim" |
find . -type f -name '*.nim' |
fd --glob '*.nim' |
find . -type f | fzf --filter '.nim' |
| Files changed in 1 day | ff "*.nim" --changed 1d |
find . -type f -name '*.nim' -mtime -1 |
fd --changed-within 1day --glob '*.nim' |
find ... -mtime -1 | fzf --filter '.nim' |
| Find files containing TODO | ff "*.py" --contains TODO |
find ... -exec grep -l TODO {} + |
fd --glob '*.py' -X grep -l TODO |
find ... | xargs grep -l TODO | fzf |
All measurements below were run by executing commands repeatedly on the same local dataset.
Benchmark dataset:
- 20,000 files across 100 directories
- Tests run with
--warmup 3 --runs 20using hyperfine
System specs:
- OS: Linux
6.19.12-1.el10.elrepo.x86_64(Red Hat Enterprise Linux 10.1) - CPU: Intel Core i7-9700 (8 cores @ 3.00GHz)
- RAM: 16 GB DDR4
- Tool versions:
ff 2.1.0,fd 10.4.2,find 4.9.0
| Pattern | ff (ms) | fd (ms) | find (ms) | Winner |
|---|---|---|---|---|
*.txt |
8.1 | 7.5 | 10.4 | fd (+8% faster) |
All files (*) |
14.6 | 7.1 | 7.3 | fd (2x faster) |
| Pattern | ff (ms) | fd (ms) | find (ms) | Winner |
|---|---|---|---|---|
*.txt -t f |
12.4 | 7.2 | 10.7 | fd (+72% faster) |
| Command | ff (ms) | fd+grep (ms) | find+grep (ms) | Winner |
|---|---|---|---|---|
*.py --contains TODO |
11.5 | 7.4 | 11.6 | fd (+56% faster) |
| Pattern | ff (ms) | fd (ms) | find (ms) | Winner |
|---|---|---|---|---|
.*\.txt$ |
15.2 | 7.3 | 10.6 | fd (2x faster) |
| Command | Time (ms) | Notes |
|---|---|---|
ff "*" (default) |
14.6 | Fastest |
ff "*" -j 4 |
22.7 | 55% slower |
ff "*" -j 8 |
22.9 | 56% slower |
- fd is faster - particularly for glob patterns and all-files traversal (2x faster)
- ff single-thread - faster than ff parallel mode (use default, no
-j) - ff built-in content search - simpler than fd+grep pipeline (but slower)
- find - surprisingly competitive for simple patterns
Fun fact, ff uses less cpu in ALMOST the speed of fd!
| Operation | Recommendation |
|---|---|
Simple glob (*.txt) |
fd --glob or find -name |
| All files | fd . |
| Content search | fd --glob | xargs grep (faster) or ff --contains (simpler) |
| Parallel work | fd (better implementation) |
- Built-in content search (no external grep needed)
- Natural language queries (
"python files modified this week") - Fuzzy matching
- Semantic symbol search
- More flexible filtering options
- No dependencies (static binary available)
fastfind also supports semantic-style symbol discovery directly from the CLI.
Run these inside a project directory:
Find function definitions:
ff --function parse .
Find classes:
ff --class Parser .
Find symbols:
ff --symbol Config .
This allows basic code discovery without launching a language server.
fastfind supports optional index-based search for faster repeated queries:
# Build initial index
ff --rebuild-index ~
# Update index incrementally (fast)
ff --update-index ~
# Verify and clean stale entries
ff --verify-index
# Check index status
ff --index-status
# Use index for searches
ff "*.py" --use-indexThe index stores file metadata (path, size, mtime, kind) for quick lookups. Incremental updates only process changed files.
In interactive mode, use these commands:
:cd <path> # Change directory
:exec <cmd> # Run command on selected files (use {} for path)
:rm # Delete selected files
:sort <key> # Sort by name/size/time
:filter <f|d|l> # Filter by type (file/dir/link)
:h # Toggle hidden files
:q # QuitExample:
# Select files with Space, then:
:exec rm {} # Delete selected files
:exec echo {} # Echo selected paths
Use ff when:
- You want one command for name, size/time, content, and git filters.
- You need fuzzy file lookup without wiring pipelines.
- You want interactive selection and direct output from the same tool.
- You want optional index-based search for repeated lookups.
- You need semantic symbol discovery (
--function,--class,--symbol) without launching a language server.
Use something else when:
- Choose
findfor highly specialized POSIX expression logic or legacy scripts that already rely on it. - Choose
fdfor minimal syntax and very fast everyday filename/path lookup. - Choose
fzfwhen you already have a candidate list and want the best interactive narrowing.
ff [OPTIONS] <pattern> [path ...]
ff "<natural language query>" [path ...]
| Area | Flags |
|---|---|
| Match mode | --glob, --regex, --fixed, --fuzzy |
| Path mode | --name, --full-path, --full-match |
| Traversal | -H, -L, -x, --gitignore, --max-depth, -j |
| Filters | --type, --size, --changed, --contains, --exclude |
| Git | --git-modified, --git-untracked, --git-tracked, --git-changed |
| Output | --long, --json, --ndjson, --table, --sort, --limit, --stats |
| Interactive/index | --interactive, --select, --use-index, --rebuild-index, --update-index, --verify-index |
# Recent logs, newest first
ff "*.log" --sort time --reverse
# Changed Python files in the current directory tree
ff "*.py" --changed 7d .
# Symbol search inside a project
ff --function parse .
# Interactive select, then open with editor
ff "*.nim" --select --exec "vim {}"If you are reading the codebase, start here:
- Entry point:
src/ff.nim - CLI parsing/config:
src/ff/cli.nim - Search engine:
src/ff/search.nim - Core types:
src/ff/core.nim - Output formatting:
src/ff/output.nim - Fuzzy matcher:
src/ff/fuzzy.nim - Index support:
src/ff/index.nim - Interactive UI:
src/ff/interactive.nim - Semantic search:
src/ff/semantic.nim - Package metadata:
fastfind.nimble - Binary output dir:
bin/
Project docs:
- Contributing guide:
CONTRIBUTING.md - Security policy:
SECURITY.md - Code of conduct:
CODE_OF_CONDUCT.md
- Linux: full support
- macOS: full support
- BSD: full support
- Windows: partial support (interactive/index behavior may differ)
Contributions are welcome.
Please read CONTRIBUTING.md first.
Wanna talk privately? email me at: robertflexxgh@gmail.com
Please report vulnerabilities using SECURITY.md.
MIT License. See LICENSE.
As a solo developer, i love and hate developing. I love developing a lot especially for the open source community, and i enjoy making free software for EVERYONE to enjoy; 0 fee. But development gets hard especially on a medium scale project as 1 guy, and if updates dont happen often, or updates are buggy, i am very sorry. I take hours out of my day strictly programming on projects i love, and care about. Especially this one, i focus a lot of my time on this and im happy its getting attention. Programming has been a wild ride for me, and a very passionate one too, as i love doing the things i do for the open source community. And the cons. Developing a lot has taken a toll on my mental health, as i barely get sleep at night due to me constantly debugging stuff lol, but i will always try to push new updates on a lot of my software. Thank you for the support!
- RobertFlexx