From 9dca9f2bcd06f921312762e0b07254f5f8f51fc2 Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@tavianator.com>
Date: Tue, 23 May 2023 14:34:07 -0400
Subject: eval: Pre-allocate the highest fd

This avoids the need to grow the fd table during the search,
significantly reducing kernel contention when opening directories in
parallel.
---
 src/eval.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

(limited to 'src')

diff --git a/src/eval.c b/src/eval.c
index b9bce6c..f16821e 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1471,6 +1471,23 @@ static int raise_fdlimit(const struct bfs_ctx *ctx) {
 	return ret;
 }
 
+/** Preallocate the fd table in the kernel. */
+static void reserve_fds(int limit) {
+	// Kernels typically implement the fd table as a dynamic array.
+	// Growing the array can be expensive, especially if files are being
+	// opened in parallel.  We can work around this by allocating the
+	// highest possible fd, forcing the kernel to grow the table upfront.
+
+#ifdef F_DUPFD_CLOEXEC
+	int fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, limit - 1);
+#else
+	int fd = fcntl(STDIN_FILENO, F_DUPFD, limit - 1);
+#endif
+	if (fd >= 0) {
+		xclose(fd);
+	}
+}
+
 /** Infer the number of file descriptors available to bftw(). */
 static int infer_fdlimit(const struct bfs_ctx *ctx, int limit) {
 	// 3 for std{in,out,err}
@@ -1604,6 +1621,7 @@ int bfs_eval(const struct bfs_ctx *ctx) {
 	}
 
 	int fdlimit = raise_fdlimit(ctx);
+	reserve_fds(fdlimit);
 	fdlimit = infer_fdlimit(ctx, fdlimit);
 
 	int nthreads;
-- 
cgit v1.2.3