Dropping eBPF CPU Cost by About 90% with Memoization (Not AI Gen)
This article details how an eBPF security agent drastically cut its CPU cost by about 90% through intelligent memoization. The core innovation involves an inode-based cache to avoid repeatedly walking file paths for policy enforcement. It's popular on HN for its deep dive into low-level systems optimization, showcasing clever solutions to performance bottlenecks in critical infrastructure.
The Lowdown
The author and their brother developed an eBPF security agent designed for speed, but discovered a significant performance bottleneck in enforcing path-based policies. The most CPU-intensive task wasn't policy enforcement itself, but rather the repeated reconstruction and traversal of file paths to determine which policy applied to a given file open, especially for frequently accessed files.
- The Problem: Prior to optimization, every file open triggered an expensive process of retrieving the file path, walking up parent dentries, checking for policies at each level, and then merging these to determine the final policy. This 'slow path' was highly inefficient for files opened multiple times or for multiple files within the same directory tree.
- The Solution: They implemented an inode-based cache using an LRU hash map. This cache stores the policy applicable to a specific inode, indexed by a unique key composed of the mount namespace ID, mount ID, and inode number, ensuring correct context across different mount points and namespaces.
- Cache Structure: The cache key combines
mntns_id,mount_id, andinodeto uniquely identify a file's context. The cache value stores anaccess_index(representing the policy as a bitmask) and astate. - Optimized Flow: With the cache, the agent first attempts a cache lookup. A cache hit allows immediate policy enforcement, bypassing the slow path. Only on a cache miss is the full path traversal performed, with the result then stored in the cache for future use.
- Performance Impact: Benchmarks demonstrated a dramatic reduction in kernel CPU cycles from 28 billion to 3.03 billion, representing a 90% cost drop. Key functions like
tail_call_security_check,is_restricted_filepath, andpath_check_callback, which previously dominated CPU usage, effectively disappeared from flamegraphs after the initial lookup. - Edge Cases and Trade-offs: The primary challenge was handling hardlinks, where multiple paths can point to the same inode, potentially leading to inaccurate cached policies. The chosen workaround involves skipping cache usage for inodes with a link count greater than one (
i_nlink != 1), prioritizing accuracy over complete cache coverage.
Ultimately, this project highlights a successful application of memoization in a highly performance-critical eBPF context. The author expressed satisfaction with the development process and noted that the internal nature of the cache means existing user policies do not need modification to benefit from the speed improvements. The optimized agent code has also been open-sourced.