#! /bin/bash
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Finds the optimal update_pch settings that results in,
# per module and library, the fastest build time and
# smallest intermediate files (.o/.obj) output.
# Usage: update_pch_autotune.sh [<module1> <module2>]
# Invoke: /opt/lo/bin/make cmd cmd="./bin/update_pch_autotune.sh [..]"
# The resulting values may be entered in update_pch
# to be use for generating PCH in the future.
# Run this script after major header changes.
root=`dirname $0`
root=`cd $root/.. && pwd`
cd $root
if test -z
"$1";
then
modules=`ls ./*/inc/pch/precompiled_*.hxx | sed -e s%./%% -e s%/.*%% | uniq`
else
modules=
"$@"
fi
if [[
"$OSTYPE" ==
"cygwin" ]];
then
MAKE=/opt/lo/bin/
make
else
MAKE=
make
fi
function build()
{
local START=$(date +%s.%N)
$
MAKE -sr
"$module" > /dev/null
status=$?
if [ $status -ne 0 ];
then
# Spurious failures happen.
$
MAKE "$module.build" > /dev/null
status=$?
fi
local
END=$(date +%s.%N1)
build_time=$(printf %.1f $(
echo "$END - $START" | bc))
size=
"FAILED"
score=
"FAILED"
if [ $status -eq 0 ];
then
# The total size of the object files.
size=
"$(du -s workdir/CxxObject/$module/ | awk '{print $1}')"
# Add the pch file size.
filename_rel=
"workdir/PrecompiledHeader/nodebug/$(basename $header)*"
filename_dbg=
"workdir/PrecompiledHeader/debug/$(basename $header)*"
if [[ $filename_rel -nt $filename_dbg ]];
then
pch_size=
"$(du -s $filename_rel | awk '{print $1}' | paste -sd+ | bc)"
else
pch_size=
"$(du -s $filename_dbg | awk '{print $1}' | paste -sd+ | bc)"
fi
size=
"$(echo "$pch_size + $size
" | bc)"
# Compute a score based on the build time and size.
# The shorter the build time, and smaller disk usage, the higher the score.
score=$(printf %.2f $(
echo "10000 / ($build_time * e($size/1048576))" | bc -l))
fi
}
function run()
{
local msg=
"$module.$libname, ${@:3}, "
printf
"$msg"
./bin/update_pch
"$module" "$libname" "${@:3}" --silent
status=$?
if [ $status -eq 0 ];
then
build
summary=
"$build_time, $size, $score"
if [ $status -eq 0 ];
then
new_best_for_cuttof=$(
echo "$score > $best_score_for_cuttof" | bc -l)
if [ $new_best_for_cuttof -eq 1 ];
then
best_score_for_cuttof=$score
fi
new_best=$(
echo "$score > $best_score" | bc -l)
if [ $new_best -eq 1 ];
then
best_score=$score
best_args=
"${@:3}"
best_time=$build_time
best_cutoff=$cutoff
summary=
"$build_time, $size, $score,*"
fi
fi
else
# Skip if pch is not updated.
summary=
"0, 0, 0"
fi
echo "$summary"
}
function args_to_table()
{
local sys=
"EXCLUDE"
local mod=
"EXCLUDE"
local loc=
"EXCLUDE"
local cutoff=0
IFS=
' ' read -r -a aargs <<< $best_args
for index in
"${!aargs[@]}"
do
if [
"${aargs[index]}" =
"--include:system" ];
then
sys=
"INCLUDE"
elif [
"${aargs[index]}" =
"--exclude:system" ];
then
sys=
"EXCLUDE"
elif [
"${aargs[index]}" =
"--include:module" ];
then
mod=
"INCLUDE"
elif [
"${aargs[index]}" =
"--exclude:module" ];
then
mod=
"EXCLUDE"
elif [
"${aargs[index]}" =
"--include:local" ];
then
loc=
"INCLUDE"
elif [
"${aargs[index]}" =
"--exclude:local" ];
then
loc=
"EXCLUDE"
elif [[
"${aargs[index]}" == *
"cutoff"* ]]
then
cutoff=$(
echo "${aargs[index]}" | grep -Po
'\-\-cutoff\=\K\d+')
fi
done
local key=$(printf
"'%s.%s'" $module $libname)
echo "$(printf " %-36s: (%2d, %s, %s, %s),
# %5.1f" $key $cutoff $sys $mod $loc $best_time)"
}
for module in $modules;
do
# Build without pch includes as sanity check.
#run "$root" "$module" --cutoff=999
# Build before updating pch.
$
MAKE "$module.build" > /dev/null
if [ $? -ne 0 ];
then
# Build with dependencies before updating pch.
echo "Failed to build $module, building known state with dependencies..."
./bin/update_pch.sh
"$module" > /dev/null
$
MAKE "$module.clean" > /dev/null
$
MAKE "$module.all" > /dev/null
if [ $? -ne 0 ];
then
# Build all!
echo "Failed to build $module with dependencies, building all..."
$
MAKE > /dev/null
if [ $? -ne 0 ];
then
>&2
echo "Broken build. Please revert changes and try again."
exit 1
fi
fi
fi
# Find pch files in the module to update.
headers=`find $root/$module/ -type f -iname
"precompiled_*.hxx"`
# Each pch belongs to a library.
for header in $headers;
do
libname=`
echo $header | sed -e s/.*precompiled_// -e s/\.hxx//`
#TODO: Backup the header and restore when last tune fails.
# Force update on first try below.
echo "Autotuning $module.$libname..."
./bin/update_pch
"$module" "$libname" --cutoff=999 --silent --force
best_score=0
best_args=
""
best_time=0
best_cutoff=0
for i in {1..16};
do
cutoff=$i
best_score_for_cuttof=0
#run "$root" "$module" "--cutoff=$i" --include:system --exclude:module --exclude:local
run
"$root" "$module" "--cutoff=$i" --exclude:system --exclude:module --exclude:local
#run "$root" "$module" "--cutoff=$i" --include:system --include:module --exclude:local
run
"$root" "$module" "--cutoff=$i" --exclude:system --
include:module --exclude:local
#run "$root" "$module" "--cutoff=$i" --include:system --exclude:module --include:local
run
"$root" "$module" "--cutoff=$i" --exclude:system --exclude:module --
include:local
#run "$root" "$module" "--cutoff=$i" --include:system --include:module --include:local
run
"$root" "$module" "--cutoff=$i" --exclude:system --
include:module --
include:local
if [ $i -gt $((best_cutoff+2)) ];
then
score_too_low=$(
echo "$best_score_for_cuttof < $best_score / 1.10" | bc -l)
if [ $score_too_low -eq 1 ];
then
echo "Score hit low of $best_score_for_cuttof, well below overall best of $best_score. Stopping."
break;
fi
fi
done
./bin/update_pch
"$module" "$libname" $best_args --force --silent
echo "> $module.$libname, $best_args, $best_time, $size, $score"
echo
table+=$
'\n'
table+=
"$(args_to_table)"
done
done
echo "Update the relevant lines in ./bin/update_pch script:"
>&2
echo "$table"
exit 0