Speeding Up eBPF File Opens by 54% (Not AI Gen) Nathan Naveen reports that small optimizations to his open-source eBPF agent sped up repeated file opens by 54% and made cold opens about 15% faster, with the changes spanning commits 9c521bd to cfc7538 in the bomfather/agent repository. The largest single win came from an inode cache that cut eBPF CPU cost by 90%, while the new tweaks include skipping the LPM trie map lookup when a path tail is shorter than the 128-byte chunk and checking the inode cache before rebuilding the file path in lsm_file_open. Naveen said he feeds performance-test results to an LLM to surface inefficiencies, though he writes the code himself. Speeding Up eBPF File Opens by 54% Not AI Gen https://nathannaveen.dev/posts/speeding-up-ebpf-file-opens/ My brother and I designed our eBPF agent to be really fast using bitmasks, etc. , but we’ve recently doubled down on performance improvements. For example, I recently implemented an inode cache https://nathannaveen.dev/posts/dropping-ebpf-cpu-cost-by-90/ https://nathannaveen.dev/posts/dropping-ebpf-cpu-cost-by-90/ , and my brother implemented longest prefix matching on our policy paths https://neilnaveen.dev/posts/speeding-up-ebpf-filepath-checks/ https://neilnaveen.dev/posts/speeding-up-ebpf-filepath-checks/ . These changes were pretty large, while the changes I’m covering in this blog post are smaller following a similar concept to the British Cycling team’s “1% better” philosophy . But these small changes sped up repeated file opens by 54% and made cold opens about 15% faster. This blog post is going to read less like a blog post and more like a changelog. I decided to share it because I thought some of these changes were cool and someone might find it interesting. Our repo is open source, so you can find everything in this blog post at https://github.com/bomfather/agent https://github.com/bomfather/agent . And everything discussed in this blog post is between commits 9c521bd and cfc7538 . How did I Find These Issues? We created a perf test when creating the inode cache, and I’ve been running it after each commit. Then I give the perf results to an LLM and ask it to find inefficiencies I can fix. I know someone is thinking to themselves that I the author wrote “Not AI Gen” in the title, and that I’m a liar. Yeah well… I’m only human, and I don’t want to go through the perf results myself, so I hand it off to my AI. Just because I don’t use AI to write my code doesn’t mean that I am oblivious to what AI is good at. The Problems As I mentioned before, these are simple changes I made to get the biggest performance improvements with the smallest amount of change, so none of these are revolutionary ideas. 1. LPM Cache Chunking I know Longest Prefix Matching LPM isn’t the most common thing to know, but I don’t want to explain it in this blog post because that isn’t what this write up is about. If you don’t know what it is, I recommend doing a bit of research about it because it is pretty cool and not too complicated to understand. Our LPM cache creates 128 byte chunks and assigns an ID to each chunk. Then it stores those IDs and the remaining part of the file path in the LPM trie map. Previously, a tail shorter than 128 bytes would still go through the lookup. So the kernel would clean the chunk map key, copy the remainder of the bytes to it, and search the map. But the key wouldn’t actually be in the map because it isn’t a full 128 byte chunk. This change is just an if statement that checks whether the chunk length is less than 128 bytes. If so, we know that we don’t have to do the entire map lookup thingy. I think this is a pretty cool change because most paths are under 128 bytes, so it removes the map miss on the one and only iteration. The code: https://github.com/bomfather/agent/commit/2754d343585c6b8acd1184bca28faf73468c21b9 https://github.com/bomfather/agent/commit/2754d343585c6b8acd1184bca28faf73468c21b9 . As I mentioned, I wanted to work on 1% improvements. So, this is an absolutely tiny change, but it has a pretty significant impact. 2. inode Check Before Path Building This was more of a fix on top of my previous solution rather than a new improvement. So… Um… In my previous blog post https://nathannaveen.dev/posts/dropping-ebpf-cpu-cost-by-90/ https://nathannaveen.dev/posts/dropping-ebpf-cpu-cost-by-90/ , I said that we checked whether the inode cache key was in the cache, and only if it wasn’t did we build the path. Yeah… Well, it turns out lsm file open always rebuilt the path first. I meant to check the cache before building the file path, but I guess I forgot to write the code to do so I’m nervously laughing while writing this . Well, my solution to this problem is to just implement the fix and sweep this debacle under the rug : So, that is exactly what I did: https://github.com/bomfather/agent/commit/d50233e1bc4a0790b8ee08dcc9196ce7d0b82328 https://github.com/bomfather/agent/commit/d50233e1bc4a0790b8ee08dcc9196ce7d0b82328 . On the upside, we saw great performance improvements even when building the file path before checking the cache, and even better performance after implementing this change. All is well that ends well. 3. Stop Attaching ptrace Only Programs Unless block ptrace Is On So, our agent can block ptrace s because you usually don’t need ptrace on a server you don’t need to enable ptrace blocking if you are actually using ptrace . This is great, but most processes read /proc/