Skip to content

pkg/fileutils: GetTotalUsedFds: reduce allocations#45848

Merged
thaJeztah merged 4 commits intomoby:masterfrom
thaJeztah:improve_fd_count
Jun 30, 2023
Merged

pkg/fileutils: GetTotalUsedFds: reduce allocations#45848
thaJeztah merged 4 commits intomoby:masterfrom
thaJeztah:improve_fd_count

Conversation

@thaJeztah
Copy link
Copy Markdown
Member

@thaJeztah thaJeztah commented Jun 29, 2023

pkg/fileutils: GetTotalUsedFds(): don't pretend to support FreeBSD

Commit 8d56108 moved this function from
the generic (no build-tags) fileutils.go to a unix file, adding "freebsd"
to the build-tags.

This likely was a wrong assumption (as other files had freebsd build-tags).
FreeBSD's procfs does not mention /proc/<pid>/fd in the manpage, and
we don't test FreeBSD in CI, so let's drop it, and make this a Linux-only
file.

While updating also dropping the import-tag, as we're planning to move
this file internal to the daemon.

pkg/fileutils: add BenchmarkGetTotalUsedFds

go test -bench ^BenchmarkGetTotalUsedFds$ -run ^$ ./pkg/fileutils/
goos: linux
goarch: arm64
pkg: github.com/docker/docker/pkg/fileutils
BenchmarkGetTotalUsedFds-5   	  149272	      7896 ns/op	     945 B/op	      20 allocs/op

pkg/fileutils: GetTotalUsedFds: reduce allocations

Use File.Readdirnames instead of os.ReadDir, as we're only interested in
the number of files, and results don't have to be sorted.

Before:

BenchmarkGetTotalUsedFds-5            149272              7896 ns/op             945 B/op         20 allocs/op

After:

BenchmarkGetTotalUsedFds-5            153517              7644 ns/op             408 B/op         10 allocs/op

pkg/fileutils: GetTotalUsedFds(): use fast-path for Kernel 6.2 and up

Linux 6.2 and up (commit f1f1f2569901ec5b9d425f2e91c09a0e320768f3)
provides a fast path for the number of open files for the process.

From the Linux docs:

The number of open files for the process is stored in 'size' member of
stat() output for /proc//fd for fast access.

This patch adds a fast-path for Kernels that support this, and falls back
to the slow path if the Size fields is zero.

Comparing on a Fedora 38 (kernel 6.2.9-300.fc38.x86_64):

Before/After:

go test -bench ^BenchmarkGetTotalUsedFds$ -run ^$ ./pkg/fileutils/
BenchmarkGetTotalUsedFds        57264     18595 ns/op     408 B/op      10 allocs/op
BenchmarkGetTotalUsedFds       370392      3271 ns/op      40 B/op       3 allocs/op

Note that the slow path has 1 more file-descriptor, due to the open
file-handle for /proc//fd during the calculation.

- What I did

- How I did it

- How to verify it

- Description for the changelog

- A picture of a cute animal (not mandatory but encouraged)

log.G(context.TODO()).Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err)
} else {
return len(fds)
nameCount, err := readDirnamesCount(fmt.Sprintf("/proc/%d/fd", os.Getpid()))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From https://docs.kernel.org/filesystems/proc.html#proc-pid-fd-list-of-symlinks-to-open-files:

The number of open files for the process is stored in 'size' member of stat() output for /proc//fd for fast access.

Copy link
Copy Markdown
Member

@neersighted neersighted Jun 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per @corhere: only on kernel 6.4+. We should use this as a fast path, and fall back to readdir(2)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference: the fast path was added to the kernel in torvalds/linux@f1f1f25

Commit 8d56108 moved this function from
the generic (no build-tags) fileutils.go to a unix file, adding "freebsd"
to the build-tags.

This likely was a wrong assumption (as other files had freebsd build-tags).
FreeBSD's procfs does not mention `/proc/<pid>/fd` in the manpage, and
we don't test FreeBSD in CI, so let's drop it, and make this a Linux-only
file.

While updating also dropping the import-tag, as we're planning to move
this file internal to the daemon.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
    go test -bench ^BenchmarkGetTotalUsedFds$ -run ^$ ./pkg/fileutils/
    goos: linux
    goarch: arm64
    pkg: github.com/docker/docker/pkg/fileutils
    BenchmarkGetTotalUsedFds-5   	  149272	      7896 ns/op	     945 B/op	      20 allocs/op

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Use File.Readdirnames instead of os.ReadDir, as we're only interested in
the number of files, and results don't have to be sorted.

Before:

    BenchmarkGetTotalUsedFds-5   	  149272	      7896 ns/op	     945 B/op	      20 allocs/op

After:

    BenchmarkGetTotalUsedFds-5   	  153517	      7644 ns/op	     408 B/op	      10 allocs/op

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Linux 6.2 and up (commit [f1f1f2569901ec5b9d425f2e91c09a0e320768f3][1])
provides a fast path for the number of open files for the process.

From the [Linux docs][2]:

> The number of open files for the process is stored in 'size' member of
> `stat()` output for /proc/<pid>/fd for fast access.

[1]: torvalds/linux@f1f1f25
[2]: https://docs.kernel.org/filesystems/proc.html#proc-pid-fd-list-of-symlinks-to-open-files

This patch adds a fast-path for Kernels that support this, and falls back
to the slow path if the Size fields is zero.

Comparing on a Fedora 38 (kernel 6.2.9-300.fc38.x86_64):

Before/After:

    go test -bench ^BenchmarkGetTotalUsedFds$ -run ^$ ./pkg/fileutils/
    BenchmarkGetTotalUsedFds        57264     18595 ns/op     408 B/op      10 allocs/op
    BenchmarkGetTotalUsedFds       370392      3271 ns/op      40 B/op       3 allocs/op

Note that the slow path has 1 more file-descriptor, due to the open
file-handle for /proc/<pid>/fd during the calculation.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Comment on lines +49 to +51
// Note that the slow path has 1 more file-descriptor, due to the open
// file-handle for /proc/<pid>/fd during the calculation.
return fdCount
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a fun one; "wait, why is there an off-by-one?" ahhhh... wait... gotcha.

@thaJeztah thaJeztah marked this pull request as ready for review June 29, 2023 23:36
@thaJeztah thaJeztah requested review from corhere and neersighted June 30, 2023 00:00
@thaJeztah thaJeztah added this to the 25.0.0 milestone Jun 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants