libtool: getting rid of 180,000 sed forks

Harald Hoyer March 05, 2015 #fedora #programming

When compiling systemd on rawhide, we noticed a significant slowdown in compile time. Investigating further, it turns out, that libtool forks an incredible amount of sed. Running perf showed 30% of the “make all” was spent in bash. strace showed an execve of sed with the same arguments 180,000 times!!!!!

Looking in the code reveals the culprit:

_G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;

and

qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`

sed_quote_subst looks like this:

sed_quote_subst='s|\([`"$\\]\)|\\\1|g'

This substitution can be easily done in bash, without forking sed. So I came up with a patch for libtool:

--- libtool.orig	2015-03-04 15:44:39.632703654 +0100
+++ libtool.fixed	2015-03-04 17:16:42.495983610 +0100
@@ -1607,7 +1607,15 @@
     while test 0 -lt $#; do
       case $1 in
         *[\\\`\"\$]*)
-	  _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
+          if test set = "${BASH_VERSION+set}"; then
+            _G_unquoted_arg=${1//\\/\\\\}
+            _G_unquoted_arg=${_G_unquoted_arg//\"/\\\"}
+            _G_unquoted_arg=${_G_unquoted_arg//\$/\\\$}
+            _G_unquoted_arg=${_G_unquoted_arg//\`/\\\`}
+          else
+              _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"`
+          fi
+          ;;
         *)
           _G_unquoted_arg=$1 ;;
       esac
@@ -5756,7 +5764,11 @@
   if test \"\$libtool_execute_magic\" != \"$magic\"; then
     file=\"\$0\""

-    qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+    if test set = "${BASH_VERSION+set}"; then
+      qECHO=$(_G=$($ECHO "$ECHO");_G=${_G//\\/\\\\};_G=${_G//\"/\\\"};_G=${_G//\$/\\\$};_G=${_G//\`/\\\`};printf '%s\n' "$_G")
+    else
+      qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+    fi
     $ECHO "\

With that patch, I compared the runtime of a compile (with ccache on). Original libtool:

$ time make -j4
[…]
real 5m23.880s
user 11m39.731s
sys 7m12.578s

Fixed libtool:

$ time make -j4
[…]
real 2m47.325s
user 9m33.561s
sys 0m57.522s

The old version spent half of the time creating a pipe, forking sed and parsing the output. Someone with connections to the libtool maintainers, please point them to this problem! Reported as bug 20006.