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

feat: use mimalloc instead of musl's mallocng for static builds #666

Merged
merged 5 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions .github/workflows/static.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ jobs:
platform: linux/amd64
qemu: false
debug: true
name: Build ${{ matrix.platform }} static binary${{ matrix.debug && ' (debug)' || '' }}
-
platform: linux/amd64
qemu: false
mimalloc: true
name: Build ${{ matrix.platform }} static binary${{ matrix.debug && ' (debug)' || '' }}${{ matrix.mimalloc && ' (mimalloc)' || '' }}
runs-on: ubuntu-latest
needs: [ prepare ]
steps:
Expand Down Expand Up @@ -121,12 +125,13 @@ jobs:
targets: static-builder
set: |
${{ matrix.debug && 'static-builder.args.DEBUG_SYMBOLS=1' || '' }}
${{ matrix.mimalloc && 'static-builder.args.MIMALLOC=1' || '' }}
*.tags=
*.platform=${{ matrix.platform }}
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' && '' }}
*.cache-from=type=gha,scope=refs/heads/main-static-builder${{ matrix.debug && '-debug' && '' }}
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' && '' }},ignore-error=true
${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' && '' }}${{ matrix.mimalloc && '-mimalloc' && '' }}
*.cache-from=type=gha,scope=refs/heads/main-static-builder${{ matrix.debug && '-debug' && '' }}${{ matrix.mimalloc && '-mimalloc' && '' }}
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' && '' }}${{ matrix.mimalloc && '-mimalloc' && '' }},ignore-error=true
${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
env:
SHA: ${{ github.sha }}
VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref || github.sha }}
Expand All @@ -145,7 +150,7 @@ jobs:
METADATA: ${{ steps.build.outputs.metadata }}
-
name: Upload metadata
if: fromJson(needs.prepare.outputs.push) && !matrix.debug
if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc
uses: actions/upload-artifact@v3
with:
name: metadata-static-builder
Expand All @@ -154,11 +159,11 @@ jobs:
retention-days: 1
-
name: Copy binary
if: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug }}
if: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug || matrix.mimalloc }}
run: |
digest=$(jq -r '."static-builder"."containerimage.config.digest"' <<< "${METADATA}")
docker create --platform=${{ matrix.platform }} --name static-builder "${digest}"
docker cp "static-builder:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}"
docker cp "static-builder:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}"
env:
METADATA: ${{ steps.build.outputs.metadata }}
BINARY: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}
Expand All @@ -167,12 +172,12 @@ jobs:
if: ${{ !fromJson(needs.prepare.outputs.push) }}
uses: actions/upload-artifact@v3
with:
name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}
path: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}
name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
path: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
-
name: Upload debug asset
if: fromJson(needs.prepare.outputs.push) && matrix.debug && (needs.prepare.outputs.ref || github.ref_type == 'tag')
run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" frankenphp-linux-x86_64-debug --repo dunglas/frankenphp --clobber
name: Upload special assets
if: fromJson(needs.prepare.outputs.push) && (matrix.debug || matrix.mimalloc) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" frankenphp-linux-x86_64${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }} --repo dunglas/frankenphp --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand Down
99 changes: 90 additions & 9 deletions build-static.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/sh

set -o errexit
set -x

if ! type "git" > /dev/null; then
echo "The \"git\" command must be installed."
Expand All @@ -15,6 +16,11 @@ if [ "${os}" = "darwin" ]; then
md5binary="md5 -q"
fi

if [ "${os}" = "linux" ] && ! type "cmake" > /dev/null; then
echo "The \"cmake\" command must be installed."
exit 1
fi

if [ -z "${PHP_EXTENSIONS}" ]; then
export PHP_EXTENSIONS="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,gd,iconv,igbinary,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sodium,sqlite3,sysvsem,tokenizer,xml,xmlreader,xmlwriter,zip,zlib"
fi
Expand All @@ -23,7 +29,7 @@ if [ -z "${PHP_EXTENSION_LIBS}" ]; then
export PHP_EXTENSION_LIBS="bzip2,freetype,libavif,libjpeg,liblz4,libwebp,libzip"
fi

# the Brotli library must always be built as it is required by http://github.com/dunglas/caddy-cbrotli
# The Brotli library must always be built as it is required by http://github.com/dunglas/caddy-cbrotli
if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bbrotli\b"; then
export PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},brotli"
fi
Expand Down Expand Up @@ -117,28 +123,103 @@ if [ "${os}" = "mac" ]; then
export CGO_LDFLAGS="-framework CoreFoundation -framework SystemConfiguration"
fi

CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a $(./buildroot/bin/php-config --ldflags) $(./buildroot/bin/php-config --libs)"
CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a $(./buildroot/bin/php-config --ldflags || true) $(./buildroot/bin/php-config --libs || true)"
dunglas marked this conversation as resolved.
Show resolved Hide resolved
export CGO_LDFLAGS

LIBPHP_VERSION="$(./buildroot/bin/php-config --version)"
export LIBPHP_VERSION

cd ../..

# Embed PHP app, if any
if [ -n "${EMBED}" ] && [ -d "${EMBED}" ]; then
tar -cf app.tar -C "${EMBED}" .
${md5binary} app.tar > app_checksum.txt
fi
cd ../

if [ "${os}" = "linux" ]; then
if [ -n "${MIMALLOC}" ]; then
# Replace musl's mallocng by mimalloc
# The default musl allocator is slow, especially when used by multi-threaded apps,
# and triggers weird bugs
# Adapted from https://www.tweag.io/blog/2023-08-10-rust-static-link-with-mimalloc/

echo 'The USE_MIMALLOC environment variable is EXPERIMENTAL.'
echo 'This option can be removed or its behavior modified at any time.'

if [ ! -f "mimalloc/out/libmimalloc.a" ]; then
if [ -d "mimalloc" ]; then
cd mimalloc/
git reset --hard
git clean -xdf
git fetch --tags
else
git clone https://github.com/microsoft/mimalloc.git
cd mimalloc/
fi

git checkout "$(git describe --tags "$(git rev-list --tags --max-count=1 || true)" || true)"

curl -f -L --retry 5 https://raw.githubusercontent.com/tweag/rust-alpine-mimalloc/b26002b49d466a295ea8b50828cb7520a71a872a/mimalloc.diff -o mimalloc.diff
patch -p1 < mimalloc.diff

mkdir -p out/
cd out/
if [ -n "${DEBUG_SYMBOLS}" ]; then
cmake \
-DCMAKE_BUILD_TYPE=Debug \
-DMI_BUILD_SHARED=OFF \
-DMI_BUILD_OBJECT=OFF \
-DMI_BUILD_TESTS=OFF \
../
else
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DMI_BUILD_SHARED=OFF \
-DMI_BUILD_OBJECT=OFF \
-DMI_BUILD_TESTS=OFF \
../
fi
make -j"$(nproc || true)"

cd ../../
fi

if [ -n "${DEBUG_SYMBOLS}" ]; then
libmimalloc_path=mimalloc/out/libmimalloc-debug.a
else
libmimalloc_path=mimalloc/out/libmimalloc.a
fi

# Patch musl library to use mimalloc
for libc_path in "/usr/local/musl/lib/libc.a" "/usr/local/musl/$(uname -m)-linux-musl/lib/libc.a" "/usr/lib/libc.a"
do
if [ ! -f "${libc_path}" ] || [ -f "${libc_path}.unpatched" ]; then
continue
fi

{
echo "CREATE libc.a"
echo "ADDLIB ${libc_path}"
echo "DELETE aligned_alloc.lo calloc.lo donate.lo free.lo libc_calloc.lo lite_malloc.lo malloc.lo malloc_usable_size.lo memalign.lo posix_memalign.lo realloc.lo reallocarray.lo valloc.lo"
echo "ADDLIB ${libmimalloc_path}"
echo "SAVE"
} | ar -M
mv "${libc_path}" "${libc_path}.unpatched"
mv libc.a "${libc_path}"
done
fi

# Increase the default stack size to prevents issues with code including many files such as Symfony containers
extraExtldflags="-Wl,-z,stack-size=0x80000"
fi

if [ -z "${DEBUG_SYMBOLS}" ]; then
extraLdflags="-w -s"
fi

cd ../

# Embed PHP app, if any
if [ -n "${EMBED}" ] && [ -d "${EMBED}" ]; then
tar -cf app.tar -C "${EMBED}" .
${md5binary} app.tar > app_checksum.txt
fi

cd caddy/frankenphp/
go env
go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags '-static-pie ${extraExtldflags}' ${extraLdflags} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'" -o "../../dist/${bin}"
Expand Down
1 change: 1 addition & 0 deletions docs/fr/static.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,5 @@ Les variables d'environnement suivantes peuvent être transmises à `docker buil
* `CLEAN` : lorsque défini, `libphp` et toutes ses dépendances sont construites à partir de zéro (pas de cache)
* `DEBUG_SYMBOLS` : lorsque défini, les symboles de débogage ne seront pas supprimés et seront ajoutés dans le binaire
* `NO_COMPRESS`: ne pas compresser le binaire avec UPX
* `MIMALLOC`: (expérimental, Linux seulement) remplace l'allocateur mallocng de musl par [mimalloc](https://github.com/microsoft/mimalloc) pour des performances améliorées
* `RELEASE` : (uniquement pour les mainteneurs) lorsque défini, le binaire résultant sera uploadé sur GitHub
1 change: 1 addition & 0 deletions docs/static.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ script to customize the static build:
* `CLEAN`: when set, libphp and all its dependencies are built from scratch (no cache)
* `NO_COMPRESS`: don't compress the resulting binary using UPX
* `DEBUG_SYMBOLS`: when set, debug-symbols will not be stripped and will be added within the binary
* `MIMALLOC`: (experimental, Linux-only) replace musl's mallocng by [mimalloc](https://github.com/microsoft/mimalloc) for improved performance
* `RELEASE`: (maintainers only) when set, the resulting binary will be uploaded on GitHub
2 changes: 2 additions & 0 deletions static-builder.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ARG PHP_EXTENSION_LIBS=''
ARG CLEAN=''
ARG EMBED=''
ARG DEBUG_SYMBOLS=''
ARG MIMALLOC=''

SHELL ["/bin/ash", "-eo", "pipefail", "-c"]

Expand All @@ -24,6 +25,7 @@ LABEL org.opencontainers.image.vendor="Kévin Dunglas"

RUN apk update; \
apk add --no-cache \
alpine-sdk \
autoconf \
automake \
bash \
Expand Down
Loading