Bfs 3.0: The Fastest Find Yet
tavianator.com
tavianator.com
Most people don’t need to be messing with system files, the ones who do, can go through the extra trouble of checking a few preferences, using command line tools, setting up plist flags, etc.
> However, FSearch doesn't automatically detect changes made to the file system and update its index then. This is on the roadmap (it's called inotify support) but it'll never work as smooth as Everything on Windows, because the Linux kernel isn't particularly good at reporting filesystem changes
https://github.com/cboxdoerfer/fsearch/issues/26
Everything is comprehensive + instant + always up-to-date, that's so awesome a combo it's a pity it's Windows only
Only when the USN journal doesn't go back enough in time will Everything rebuild its database by parsing the MFT (which is also much quicker than traversing the file system).
https://learn.microsoft.com/en-us/windows-server/administrat...:
“As files, directories, and other NTFS objects are added, deleted, and modified, NTFS enters records into the USN change journal, one for each volume on the computer.”
edit: catfish is a gui front-end available for locate.
Edit: Found some answers (https://www.voidtools.com/faq/):
Re indexing:
> How long will it take to index my files?
> "Everything" only indexes file and folder names and generally takes a few seconds to build its database. A fresh install of Windows 10 (about 120,000 files) will take about 1 second to index. 1,000,000 files will take about 1 minute.
Re changes:
> Does "Everything" monitor file system changes?
> Yes, "Everything" does monitor your file systems for all changes. Your search results will update in real-time to reflect any changes. Everything will automatically keep your NTFS indexes up to date with the NTFS USN Journal. Changes will not be missed when Everything is not running as the system maintains the NTFS USN Journal.
But this tests perf for only finding one file. bfs is optimized for finding many files at once, as it defers closing the dirhandle, and uses io_uring for parallel IO callbacks.
I can see you are using opendir and closedir functions? What is the benefit from using the opendir function[1] when readdir[2] can be called on a location directly? Is the benefit that opendir returns a file descriptor for use in opening a stream to gather directory object descriptors?
[1] https://man7.org/linux/man-pages/man3/opendir.3.html
[2] https://man7.org/linux/man-pages/man3/readdir.3.html
Your project is probably more mature but if you want an alternate approach to examine here is I have been doing it: https://github.com/prettydiff/share-file-systems/blob/master...
I considering changing my use of readdir to use the withFileTypes option so that it returns a list of directory entries (objects of artifact name and type) instead of a list of conditions to discern types like I am doing on lines 382-432.
I'm not sure if this is me not using the tool how I should, me not using Linux how I should, me using the wrong tool for this job, something missing from the tool or something else entirely. I wonder if other people have this similar "double usage issue", and I'm interested in ways to avoid it.
Disclaimer: I am the developer of fdir.
tavianator@tachyon $ cat bench.mjs
#!/usr/bin/env node
import { fdir } from "fdir";
console.log(new fdir().withFullPaths().crawl("/home/tavianator/code/android").sync().length);
tavianator@tachyon $ hyperfine -w1 "node ./bench.mjs" "bfs ~/code/android -false"
Benchmark 1: node ./bench.mjs
Time (mean ± σ): 2.073 s ± 0.031 s [User: 1.372 s, System: 1.260 s]
Range (min … max): 2.022 s … 2.128 s 10 runs
Benchmark 2: bfs ~/code/android -false
Time (mean ± σ): 417.5 ms ± 6.8 ms [User: 592.3 ms, System: 2487.7 ms]
Range (min … max): 405.2 ms … 429.7 ms 10 runs
Summary
bfs ~/code/android -false ran
4.97 ± 0.11 times faster than node ./bench.mjs
Is there a way to call it that doesn't require holding all the paths in memory simultaneously?console.log(await new fdir().onlyCounts().crawl("/home/tavianator/code/android").withPromise());
$ hyperfine -w1 "NODE_ENV=production node ./fdir.mjs" "./bin/bfs /
home/thecodrr/ -false"
Benchmark 1: NODE_ENV=production node ./fdir.mjs
Time (mean ± σ): 965.5 ms ± 53.0 ms [User: 703.0 ms, System: 1220.5 ms]
Range (min … max): 858.4 ms … 1041.3 ms 10 runs
Benchmark 2: ./bin/bfs /home/thecodrr/ -false
Time (mean ± σ): 1.530 s ± 0.127 s [User: 0.341 s, System: 2.282 s]
Range (min … max): 1.401 s … 1.808 s 10 runs
Summary
'NODE_ENV=production node ./fdir.mjs' ran
1.58 ± 0.16 times faster than './bin/bfs /home/thecodrr/ -false'
$ cat fdir.mjs
#!/usr/bin/env node
import { fdir } from "fdir";
console.log(await new fdir().onlyCounts().crawl("/home/thecodrr").withPromise());
For some reason, reducing the UV_THREADPOOL_SIZE to 2 gives the best result on my machine (I have heard the opposite in case of macOS): $ hyperfine -w1 "UV_THREADPOOL_SIZE=2 NODE_ENV=production node ./fdir.mjs" "NODE_ENV=production node ./fdir.mjs" "./bin/bfs /home/thecodrr/ -false"
Benchmark 1: UV_THREADPOOL_SIZE=2 NODE_ENV=production node ./fdir.mjs
Time (mean ± σ): 355.8 ms ± 16.1 ms [User: 479.4 ms, System: 356.0 ms]
Range (min … max): 328.3 ms … 387.5 ms 10 runs
Benchmark 2: NODE_ENV=production node ./fdir.mjs
Time (mean ± σ): 935.4 ms ± 52.7 ms [User: 695.8 ms, System: 1176.5 ms]
Range (min … max): 850.6 ms … 1031.9 ms 10 runs
Benchmark 3: ./bin/bfs /home/thecodrr/ -false
Time (mean ± σ): 1.534 s ± 0.104 s [User: 0.353 s, System: 2.307 s]
Range (min … max): 1.428 s … 1.773 s 10 runs
Summary
'UV_THREADPOOL_SIZE=2 NODE_ENV=production node ./fdir.mjs' ran
2.63 ± 0.19 times faster than 'NODE_ENV=production node ./fdir.mjs'
4.31 ± 0.35 times faster than './bin/bfs /home/thecodrr/ -false'
Another factor to take into account is that I ran all this on a WSL instance which may or may not affect the performance. However, since both programs are running on WSL, the results should be accurate.I find this statement very confusing.
Typically I'm looking for files in subdirs because I'm not aware of target's path. That's depth first (though of couse there is a change I'm going to find my target quite close to the starting point).
And if I'm looking in the same dir I'm currently in (or I know what dir to look at) - then there is no need for a sophisticated search anyway, as typicall you don't have too many files in a directory unless this is some kind of tmp or cache directory.
UPD: tavianator actually is one of the fd maintainers as well: https://www.reddit.com/r/linux/comments/153far4/comment/jskx...
So it enger first subdir, looks at files there, then goes back up, enters second subdirectory, looks at files there, ... After checking each subdir it goes into the first subdir oft the first subdir, looks at files there, then goes to second subdir of first subdir
In the 1st part of the "Features" section, the "bfs" and "find" columns' contents are swapped. I was a bit confused while reading it and it took me a while to notice that it was a mistake.
I wonder how this compares in speed.
However as a disclosure, I was running this on my "bedside laptop" which is a Core2 from like 15 years ago.