ggreer@boron:~/code% du -sh .
8.3G .
ggreer@boron:~/code% time ag cpu_set_t
ag cpu_set_t 4.45s user 5.25s system 295% cpu 3.285 total
ggreer@boron:~/code% time grab -R cpu_set_t .
grab -R cpu_set_t . 13.31s user 21.67s system 35% cpu 1:38.28 total
30x faster, but these benchmarks aren't a fair fight. Ag ignores binary and hidden files by default. If I tell ag to do an unrestricted search, it's still 2x faster (43 seconds vs 98 seconds). Even with cold caches (echo 3 | sudo tee /proc/sys/vm/drop_caches between each run), ag beats grab handily: ag -u cpu_set_t 19.62s user 32.56s system 90% cpu 57.433 total
grab -R cpu_set_t . 15.48s user 37.89s system 37% cpu 2:22.67 total
I haven't profiled grab yet, but there's definitely some low-hanging fruit. For example, it looks like grab could get a big speedup by detecting literal patterns and using strstr() instead of a whole PCRE engine. Also, FileGrep::find is calling pthread_mutex_lock/unlock even if there are no matches to print. Adding a condition around that makes grab 1.5x faster.I'm glad I took a look at grab. Despite its shortcomings, I learned from it. I'll definitely try out a few tricks grab uses that ag doesn't, such as thread affinity.
One more thing: The author of grab is right about counting newlines. It does hurt performance. Still, I enable it by default in ag. I think the tradeoff is worthwhile.
1. https://github.com/ggreer/the_silver_searcher
Edit: The mutex locking change was so straightforward that I submitted a PR: https://github.com/stealth/grab/pull/2