Skip to content

ZEEK-ONLY: avoid initialization of every calloc'd int in fd_map#2

Closed
ckreibich wants to merge 1 commit intozeek-5.2.0-dev.51from
topic/christian/no-fdmap-initialization
Closed

ZEEK-ONLY: avoid initialization of every calloc'd int in fd_map#2
ckreibich wants to merge 1 commit intozeek-5.2.0-dev.51from
topic/christian/no-fdmap-initialization

Conversation

@ckreibich
Copy link
Copy Markdown
Member

@ckreibich ckreibich commented Jun 2, 2025

I keep running into the problem of Zeek gobbling up gigabytes of memory in Docker due to libkqueue's over-eager array allocations of the maximum number of file descriptors. This change is the smallest I could come up with to avoid that problem, simply by avoiding initialization of every int in a calloc()'d array.

Memory use of a regular Zeek in Docker, per htop (note resident memory):

    PID USER       PRI  NI  VIRT   RES   SHR S  CPU% MEM%▽  TIME+  Command (merged)
  42741 root        20   0 17.0G 4357M  176M S   0.0  6.8  0:01.26 zeek -i lo

Memory use of a build with this patch in Docker:

    PID USER       PRI  NI  VIRT   RES   SHR S  CPU% MEM%▽  TIME+  Command (merged)
  42460 root        20   0 17.0G  263M  177M S   0.0  0.4  0:00.21 zeek -i lo

Memory use outside of Docker, on the same system (with ulimit of 1024 fds):

    PID USER       PRI  NI  VIRT   RES   SHR S  CPU% MEM%▽  TIME+  Command (merged)
  47002 root        20   0 1083M  292M  202M S   0.0  0.5  0:00.63 zeek -i lo

See commit for details. @timwoj I wasn't quite sure where to point the merge at, does this work for you?

libkqueue allocates the the number of available file descriptors for the fd_map
array in linux/platform.c to. That limit is vast in Docker, for complicated
reasons:

# ulimit -n
1073741816

The library allocates arrays of that size in several places, but in only one
does it then also _initialize_ every member of the array (to -1). Since
calloc/mmap are optimized to allocate lazily, this means that only that instance
winds up immediately actually using ~4GB in memory.

The initialization of the int array uses -1 to indicate that a field isn't in
use. The patch changes this to use 0 as unused, simply bumping the stored fds by
one upon insert and decrementing by one on access, via helpers.
@timwoj
Copy link
Copy Markdown
Member

timwoj commented Jun 2, 2025

I wasn't quite sure where to point the merge at, does this work for you?

It should just point at master and we'll make a new branch for where it gets mapped into zeek.

I'm confused by how this works though. It's still calling calloc on the massive array. Is it simply the fact that you're not setting them all to a non-zero value the part that makes it not think it's using the memory? Also, what's the need for the addition/subtraction in the get/set methods?

@ckreibich
Copy link
Copy Markdown
Member Author

Will do, Tim.

The calloc() alone doesn't actually use the allocated memory. At least on Linux calloc() is optimized to return a single, zero-filled page on read access, and copy-on-write (and thus allocate) only when you write other values. The initialization to -1 that the code did thus ensured that the full 4GB were really actually using up memory.

The +1/-1 is just so the surrounding checks for -1 (there's only one) keep working.

I think one could go to town on all of this code a bunch more (given there's a separate array implementing a use count, but thankfully keeping the default to 0), and I actually started out implementing an abstraction that initially allocates a small chunk and then reallocs as needed as larger fds come in. But this had me thinking it's not actually needed.

@ckreibich
Copy link
Copy Markdown
Member Author

Closing in favor of #3 so it's an isolated change on top of master.

@ckreibich ckreibich closed this Jun 2, 2025
@ckreibich ckreibich deleted the topic/christian/no-fdmap-initialization branch June 5, 2025 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants