Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setup-hooks/strip: parallelise stripping #207101

Merged
merged 1 commit into from
Jul 18, 2023

Conversation

lheckemann
Copy link
Member

Description of changes

This makes bootstrapping to GNU hello ~1-2% faster on an 8-core machine and ~3-4% faster on a 64-core machine (unscientific comparison, total of 4 samples or something).

Things done
  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 23.05 Release Notes (or backporting 22.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
    • (Release notes changes) Ran nixos/doc/manual/md-to-db.sh to update generated release notes
  • Fits CONTRIBUTING.md.

Sorry, something went wrong.

@@ -62,7 +62,12 @@ stripDirs() {
if [ -n "${paths}" ]; then
echo "stripping (with command $cmd and flags $stripFlags) in $paths"
# Do not strip lib/debug. This is a directory used by setup-hooks/separate-debug-info.sh.
find $paths -type f -a '!' -wholename "$prefix/lib/debug/*" -exec $cmd $stripFlags '{}' \; 2>/dev/null
find $paths -type f -a '!' -path "$prefix/lib/debug/*" -print0 |
xargs -r -0 -n1 -P "$NIX_BUILD_CORES" $cmd $stripFlags || exit_code=$?
Copy link
Member

@alyssais alyssais Dec 21, 2022

Choose a reason for hiding this comment

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

This could be simplified to just this, right?

            xargs -r -0 -n1 -P "$NIX_BUILD_CORES" $cmd $stripFlags || [[ $? -eq 123 ]]

Copy link
Member

@grahamc grahamc Dec 21, 2022

Choose a reason for hiding this comment

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

I wonder even if we care about the exit code at all: do we care if all the files failed to strip? The old behavior did not:

$ find . -exec false \;; echo $?
0

I think we can || true this.

Copy link
Member

Choose a reason for hiding this comment

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

What Linus has here (and my suggestion also) does match the old behaviour. A 123 exit means that strip failed, and we are indeed ignoring that.

But any other error code from xargs means that xargs itself failed. We don't want to ignore that, as it likely means we're holding it wrong. Similarly, we weren't ignoring failures from find itself before.

Copy link
Member

@Artturin Artturin Jun 29, 2023

Choose a reason for hiding this comment

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

can't push

diff --git a/pkgs/build-support/setup-hooks/strip.sh b/pkgs/build-support/setup-hooks/strip.sh
index 8ee212be686..675cf2310a4 100644
--- a/pkgs/build-support/setup-hooks/strip.sh
+++ b/pkgs/build-support/setup-hooks/strip.sh
@@ -63,11 +63,12 @@ stripDirs() {
         echo "stripping (with command $cmd and flags $stripFlags) in $paths"
         # Do not strip lib/debug. This is a directory used by setup-hooks/separate-debug-info.sh.
         find $paths -type f -a '!' -path "$prefix/lib/debug/*" -print0 |
-            xargs -r -0 -n1 -P "$NIX_BUILD_CORES" $cmd $stripFlags || exit_code=$?
-        # xargs exits with status code 123 if some but not all of the
-        # processes fail. We don't care if some of the files couldn't
-        # be stripped, so ignore specifically this code.
-        [[ "$exit_code" = 123 || -z "$exit_code" ]]
+            xargs -r -0 -n1 -P "$NIX_BUILD_CORES" $cmd $stripFlags || [[ $? -eq 123 ]]
+            #                                                               ^
+            # xargs exits with status code 123 if some but not all of the
+            # processes fail. We don't care if some of the files couldn't
+            # be stripped, so ignore specifically this code.
+
         # 'strip' does not normally preserve archive index in .a files.
         # This usually causes linking failures against static libs like:
         #   ld: ...-i686-w64-mingw32-stage-final-gcc-13.0.0-lib/i686-w64-mingw32/lib/libstdc++.dll.a:

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

I intentionally didn't apply this because I find it a little clearer and the comment feels less awkward this way, sorry I forgot to write that.

@grahamc
Copy link
Member

grahamc commented Dec 21, 2022

Do we have another 1-4% improvement a few lines below?:

        find $paths -name '*.a' -type f -exec $ranlibCmd '{}' \; 2>/dev/null

@lheckemann
Copy link
Member Author

lheckemann commented Dec 21, 2022

@grahamc probably not, since there's a lot less to do:

[linus@geruest:~/scratch]$ nix-store -qR $(nix-instantiate '<nixpkgs>' -A hello) | xargs nix build
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

[linus@geruest:~/scratch]$ find result*/ -name \*.a | wc -l
49

[linus@geruest:~/scratch]$ find result*/{bin,lib,libexec,lib64,sbin} -type f | wc -l
11357

We could do it as a matter of principle though.

EDIT: we can probably make savings like this on patchShebangs and patchELF though, I'll look into this.

@alyssais alyssais added the 2.status: merge conflict This PR has merge conflicts with the target branch label May 19, 2023
@lheckemann lheckemann requested a review from RaitoBezarius May 26, 2023 22:46
@ofborg ofborg bot removed the 2.status: merge conflict This PR has merge conflicts with the target branch label Jun 28, 2023
@SuperSandro2000
Copy link
Member

Can we merge this or are there still things to do? 👀

@lheckemann
Copy link
Member Author

lheckemann commented Jun 30, 2023

@SuperSandro2000 I rebased it the other day and remove the extra parallelisation in hopes that that would make this mergeable as is :) I don't see any further things to address.

This makes bootstrapping to GNU hello ~1-2% faster on an 8-core
machine and ~3-4% faster on a 64-core machine.
@Artturin Artturin merged commit bba1411 into NixOS:staging Jul 18, 2023
@lheckemann lheckemann deleted the parallel-strip branch July 19, 2023 12:22
@trofi
Copy link
Contributor

trofi commented Jul 19, 2023

Bisect claims that 70945eb setup-hooks/strip: parallelise stripping broke config.system.build.extraUtils build in nixos (or a nixosTests.systemd-boot.basic, but it has bigger closure):

$ nix build -f. nixosTests.systemd-boot.basic -L
...
error: builder for '/nix/store/z49b3d7mj099bnggk224fpxsnsi6w9g7-extra-utils.drv' failed with exit code 1;
       last 10 log lines:
       > Copying libs for executable /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/bin/lvm
       > '/nix/store/ikvpyzyygbdvb5p5a27136r7glwf6rqm-systemd-minimal-253.6/lib/libsystemd.so.0.36.0' -> '/nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/lib/libsystemd.so.0'
       > '/nix/store/51xaj8ksganbv0lj23df2yp6b79fsfhb-libaio-0.3.113/lib/libaio.so.1.0.2' -> '/nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/lib/libaio.so.1'
       > Copying libs for executable /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/bin/scsi_id
       > Copying libs for executable /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/bin/systemd-sysctl
       > Copying libs for executable /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/bin/tune2fs
       > Copying libs for executable /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/bin/udevadm
       > Copying libs for executable /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/bin/v4l_id
       > Copying libs for executable /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/lib/ld-linux-x86-64.so.2
       > stripping (with command  and flags -s) in  /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/lib /nix/store/m1lq76v3jp321nyvf02x09v69xm97prh-extra-utils/bin
       For full logs, run 'nix log /nix/store/z49b3d7mj099bnggk224fpxsnsi6w9g7-extra-utils.drv'.

Might have something to do with multiple symlinks on the same binary.

@Artturin
Copy link
Member

#244400 will print the error to make issues easier to debug

@trofi
Copy link
Contributor

trofi commented Jul 19, 2023

Thank you! That prints the following error:

extra-utils> stripping (with command  and flags -s) in  /nix/store/2gfapyy8nldpm7sj0mw88454fwzb995m-extra-utils/lib /nix/store/2gfapyy8nldpm7sj0mw88454fwzb995m-extra-utils/bin
extra-utils> find: 'standard output': Broken pipe
extra-utils> find: write error
extra-utils> xargs: option requires an argument -- 's'
extra-utils> Try 'xargs --help' for more information.

It looks like runCommand there uses stdenvNoCC rather than stdenv and probably uses busybox for find / xargs and friends.

@trofi
Copy link
Contributor

trofi commented Jul 19, 2023

$ nix-shell -p busybox

[nix-shell:~]$ busybox sh
[nix-shell:~]$ xargs --help
BusyBox v1.36.1 () multi-call binary.

Usage: xargs [OPTIONS] [PROG ARGS]

Run PROG on every item given by stdin

        -0      NUL terminated input
        -a FILE Read from FILE instead of stdin
        -o      Reopen stdin as /dev/tty
        -r      Don't run command if input is empty
        -t      Print the command on stderr before execution
        -p      Ask user whether to run each command
        -E STR,-e[STR]  STR stops input processing
        -I STR  Replace STR within PROG ARGS with input line
        -n N    Pass no more than N args to PROG
        -s N    Pass command line of no more than N bytes
        -P N    Run up to N PROGs in parallel
        -x      Exit if size is exceeded

@@ -62,7 +62,12 @@ stripDirs() {
if [ -n "${paths}" ]; then
echo "stripping (with command $cmd and flags $stripFlags) in $paths"
# Do not strip lib/debug. This is a directory used by setup-hooks/separate-debug-info.sh.
find $paths -type f -a '!' -wholename "$prefix/lib/debug/*" -exec $cmd $stripFlags '{}' \; 2>/dev/null
find $paths -type f -a '!' -path "$prefix/lib/debug/*" -print0 |
xargs -r -0 -n1 -P "$NIX_BUILD_CORES" $cmd $stripFlags 2>/dev/null || exit_code=$?
Copy link
Member

Choose a reason for hiding this comment

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

@trofi maybe a -- before cmd is needed?

Copy link
Member

@Artturin Artturin Jul 20, 2023

Choose a reason for hiding this comment

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

Added to my PR, building.. nixosTests.systemd-boot.basic.config.nodes.machine.system.build.extraUtils

Copy link
Member

Choose a reason for hiding this comment

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

Now it fails with xargs: -s: No such file or directory

Copy link
Member

Choose a reason for hiding this comment

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

Aha there's no $cmd
stripping (with command and flags -s) in
echo "stripping (with command $cmd and flags $stripFlags) in $paths"

Copy link
Member

Choose a reason for hiding this comment

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

On current master(3ec081d) it has the same issue
extra-utils> stripping (with command and flags -s) in

so this issue was just being ignored!

Copy link
Member

Choose a reason for hiding this comment

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

@trofi
Copy link
Contributor

trofi commented Jul 30, 2023

I don't have a strong evidence, but I have a suspiction that we still manage to run multiple strip commands against a single binary if it's available through multiple symlinks: #246147 (comment)

I wonder if it would be safer to do realpath | uniq first to at least decrease workload on strip for heavily symlinked trees.

@trofi
Copy link
Contributor

trofi commented Jul 30, 2023

I'll try to test the following locally and propose it if it does not break stuff:

--- a/pkgs/build-support/setup-hooks/strip.sh
+++ b/pkgs/build-support/setup-hooks/strip.sh
@@ -68,6 +68,11 @@ stripDirs() {
         striperr="$(mktemp 'striperr.XXXXXX')"
         # Do not strip lib/debug. This is a directory used by setup-hooks/separate-debug-info.sh.
         find $paths -type f -a '!' -path "$prefix/lib/debug/*" -print0 |
+            # Make sure we process files under symlinks only once. Otherwise
+            # 'strip` can corrupt files when writes to them in parallel:
+            #   https://github.com/NixOS/nixpkgs/issues/246147#issuecomment-1657072039
+            xargs realpath | uniq |
+
             xargs -r -0 -n1 -P "$NIX_BUILD_CORES" -- $cmd $stripFlags 2>"$striperr" || exit_code=$?
         # xargs exits with status code 123 if some but not all of the
         # processes fail. We don't care if some of the files couldn't

@trofi
Copy link
Contributor

trofi commented Jul 30, 2023

Proposed the change as #246164

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.

None yet

6 participants