diff --git a/CHANGELOG.md b/CHANGELOG.md index 57984c49..fef6c13f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,9 @@ * Warn when it appears the primary font is not monospaced. Can be disabled by setting `[tweak].font-monospace-warn=no` (https://codeberg.org/dnkl/foot/issues/704). +* PGO build scripts, in the `pgo` directory. See INSTALL.md - + _Performance optimized, PGO_, for details + (https://codeberg.org/dnkl/foot/issues/701). ### Changed diff --git a/INSTALL.md b/INSTALL.md index 9543948e..40b128e7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -274,6 +274,32 @@ slower!) binary. #### Performance optimized, PGO +There are a lot more steps involved in a PGO build, and for this +reason there are a number of helper scripts available. + +`pgo/pgo.sh` is a standalone script that pieces together the other +scripts in the `pgo` directory to do a complete PGO build. This script +is intended to be used when doing manual builds. + +Example: + +```sh +cd foot +./pgo/pgo.sh auto . /tmp/foot-pgo-build-output +``` + +(run `./pgo/pgo.sh` to get help on usage) + +It supports a couple of different PGO builds; partial (covered in +detail below), full (also covered in detail below), and (full) +headless builds using Sway or cage. + +Packagers may want to use it as inspiration, but may choose to support +only a specific build type; e.g. full/headless with Sway. + +To do a manual PGO build, instead of using the script(s) mentioned +above, detailed instructions follows: + First, configure the build directory: ```sh diff --git a/PKGBUILD b/PKGBUILD index b05d7078..4cff4a6c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,3 +1,5 @@ +PGO=auto # auto|none|partial|full-current-session|full-headless-sway|full-headless-cage + pkgname=('foot-git' 'foot-terminfo-git') pkgver=1.9.0 pkgrel=1 @@ -14,80 +16,7 @@ pkgver() { } build() { - local compiler=other - local do_pgo=no - - # makepkg uses -O2 by default, but we *really* want -O3 - CFLAGS+=" -O3" - - # Figure out which compiler we're using, and whether or not to do PGO - case $(${CC-cc} --version) in - *GCC*) - compiler=gcc - do_pgo=yes - ;; - - *clang*) - compiler=clang - - # We need llvm to be able to manage the profiling data - if command -v llvm-profdata > /dev/null; then - do_pgo=yes - - # Meson adds -fprofile-correction, which Clang doesn't - # understand - CFLAGS+=" -Wno-ignored-optimization-argument" - fi - ;; - esac - - meson \ - --prefix=/usr \ - --buildtype=release \ - --wrap-mode=nofallback \ - -Db_lto=true .. - - if [[ ${do_pgo} == yes ]]; then - find -name "*.gcda" -delete - meson configure -Db_pgo=generate - ninja - - # If fcft/tllist are subprojects, we need to ensure their tests - # have been executed, or we’ll get “profile count data file not - # found” errors. - ninja test - - local script_options="--scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel" - - tmp_file=$(mktemp) - - if [[ -v WAYLAND_DISPLAY ]]; then - ./footclient --version - ./foot \ - --config /dev/null \ - --term=xterm \ - sh -c "../scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}" - else - ./footclient --version - ./foot --version - ../scripts/generate-alt-random-writes.py \ - --rows=67 \ - --cols=135 \ - ${script_options} \ - ${tmp_file} - ./pgo ${tmp_file} ${tmp_file} ${tmp_file} - fi - - rm "${tmp_file}" - - if [[ ${compiler} == clang ]]; then - llvm-profdata merge default_*profraw --output=default.profdata - fi - - meson configure -Db_pgo=use - fi - - ninja + ../pgo/pgo.sh ${PGO} .. . --prefix=/usr --wrap-mode=nofallback } check() { diff --git a/pgo/full-current-session.sh b/pgo/full-current-session.sh new file mode 100755 index 00000000..363ee793 --- /dev/null +++ b/pgo/full-current-session.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -eux + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +"${srcdir}"/pgo/full-inner.sh "${srcdir}" "${blddir}" diff --git a/pgo/full-headless-cage.sh b/pgo/full-headless-cage.sh new file mode 100755 index 00000000..ad5d0264 --- /dev/null +++ b/pgo/full-headless-cage.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -eux + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +runtime_dir=$(mktemp -d) +trap "rm -rf '${runtime_dir}'" EXIT INT HUP TERM + +XDG_RUNTIME_DIR="${runtime_dir}" WLR_BACKENDS=headless cage "${srcdir}"/pgo/full-inner.sh "${srcdir}" "${blddir}" + +# Cage’s exit code doesn’t reflect our script’s exit code +[ -f "${blddir}"/pgo-ok ] || exit 1 diff --git a/pgo/full-headless-sway-inner.sh b/pgo/full-headless-sway-inner.sh new file mode 100755 index 00000000..dd612a2a --- /dev/null +++ b/pgo/full-headless-sway-inner.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -ux + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +"${srcdir}"/pgo/full-inner.sh "${srcdir}" "${blddir}" +swaymsg exit diff --git a/pgo/full-headless-sway.sh b/pgo/full-headless-sway.sh new file mode 100755 index 00000000..bbf038b9 --- /dev/null +++ b/pgo/full-headless-sway.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -eux + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +runtime_dir=$(mktemp -d) +sway_conf=$(mktemp) + +cleanup() { + rm -f "${sway_conf}" + rm -rf "${runtime_dir}" +} +trap cleanup EXIT INT HUP TERM + +# Generate a custom config that executes our generate-pgo-data script +> "${sway_conf}" echo "exec '${srcdir}'/pgo/full-headless-sway-inner.sh '${srcdir}' '${blddir}'" + +# Run Sway. full-headless-sway-inner.sh ends with a ‘swaymsg exit’ +XDG_RUNTIME_DIR="${runtime_dir}" WLR_BACKENDS=headless sway -c "${sway_conf}" + +# Sway’s exit code doesn’t reflect our script’s exit code +[ -f "${blddir}"/pgo-ok ] || exit 1 diff --git a/pgo/full-inner.sh b/pgo/full-inner.sh new file mode 100755 index 00000000..37cb2fb6 --- /dev/null +++ b/pgo/full-inner.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +set -eux + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +. "${srcdir}"/pgo/options + +pgo_data=$(mktemp) +trap "rm -f '${pgo_data}'" EXIT INT HUP TERM + +rm -f "${blddir}"/pgo-ok + +# To ensure profiling data is generated in the build directory +cd "${blddir}" + +LC_CTYPE=en_US.UTF-8 "${blddir}"/footclient --version +LC_CTYPE=en_US.UTF-8 "${blddir}"/foot \ + --config=/dev/null \ + --term=xterm \ + sh -c " + set -eux + + '${srcdir}/scripts/generate-alt-random-writes.py' \ + ${script_options} \"${pgo_data}\" + + cat \"${pgo_data}\" + " +touch "${blddir}"/pgo-ok diff --git a/pgo/options b/pgo/options new file mode 100644 index 00000000..3fb821e9 --- /dev/null +++ b/pgo/options @@ -0,0 +1 @@ +script_options="--scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel" diff --git a/pgo/partial.sh b/pgo/partial.sh new file mode 100755 index 00000000..c16de324 --- /dev/null +++ b/pgo/partial.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -eux + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +. "${srcdir}"/pgo/options + +pgo_data=$(mktemp) +trap "rm -f ${pgo_data}" EXIT INT HUP TERM + +rm -f "${blddir}"/pgo-ok + +"${srcdir}"/scripts/generate-alt-random-writes.py \ + --rows=67 \ + --cols=135 \ + ${script_options} \ + "${pgo_data}" + +# To ensure profiling data is generated in the build directory +cd "${blddir}" + +"${blddir}"/footclient --version +"${blddir}"/foot --version +"${blddir}"/pgo "${pgo_data}" + +touch "${blddir}"/pgo-ok diff --git a/pgo/pgo.sh b/pgo/pgo.sh new file mode 100755 index 00000000..57a016cc --- /dev/null +++ b/pgo/pgo.sh @@ -0,0 +1,112 @@ +#!/bin/sh + +set -eu + +usage_and_die() { + echo "Usage: ${0} none|partial|full-current-session|full-headless-sway|full-headless-cage|[auto] [meson options]" + exit 1 +} + +[ ${#} -ge 3 ] || usage_and_die + +mode=${1} +srcdir=$(realpath "${2}") +blddir=$(realpath "${3}") +shift 3 + +# if [ -e "${blddir}" ]; then +# echo "error: ${blddir}: build directory already exists" +# exit 1 +# fi + +compiler=other +do_pgo=no + +CFLAGS="${CFLAGS-} -O3" + +case $(${CC-cc} --version) in + *GCC*) + compiler=gcc + do_pgo=yes + ;; + + *clang*) + compiler=clang + + if command -v llvm-profdata > /dev/null; then + do_pgo=yes + CFLAGS="${CFLAGS} -Wno-ignored-optimization-argument" + fi + ;; +esac + +case ${mode} in + partial|full-current-session|full-headless-sway|full-headless-cage) + ;; + + none) + do_pgo=no + ;; + + auto) + # TODO: once Sway 1.6.2 has been released, prefer + # full-headless-sway + + if [ -n "${WAYLAND_DISPLAY+x}" ]; then + mode=full-current-session + # elif command -v sway > /dev/null; then # Requires 1.6.2 + # mode=full-headless-sway + elif command -v cage > /dev/null; then + mode=full-headless-cage + else + mode=partial + fi + ;; + + *) + usage_and_die + ;; +esac + +set -x + +# echo "source: ${srcdir}" +# echo "build: ${blddir}" +# echo "compiler: ${compiler}" +# echo "mode: ${mode}" +# echo "CFLAGS: ${CFLAGS}" + +export CFLAGS +meson setup --buildtype=release -Db_lto=true "${@}" "${blddir}" "${srcdir}" + +if [ ${do_pgo} = yes ]; then + find "${blddir}" \ + '(' \ + -name "*.gcda" -o \ + -name "*.profraw" -o \ + -name default.profdata \ + ')' \ + -delete + + meson configure "${blddir}" -Db_pgo=generate + ninja -C "${blddir}" + + # If fcft/tllist are subprojects, we need to ensure their tests + # have been executed, or we’ll get “profile count data file not + # found” errors. + ninja -C "${blddir}" test + + # Run mode-dependent script to generate profiling data + "${srcdir}"/pgo/${mode}.sh "${srcdir}" "${blddir}" + + if [ ${compiler} = clang ]; then + llvm-profdata \ + merge \ + "${blddir}"/default_*.profraw \ + --output="${blddir}"/default.profdata + fi + + meson configure "${blddir}" -Db_pgo=use +fi + +ninja -C "${blddir}"