grpc: pecl install is slow and produces massive (55Mb) binary

What version of gRPC and what language are you using?

  • grpc 1.30.0
  • php 7.2.24
  • pecl 1.10.12

What operating system (Linux, Windows,…) and version?

Ubuntu 18.08

What runtime / compiler are you using (e.g. python version or version of gcc)

pecl

What did you do?

time sudo pecl install grpc

What did you expect to see?

The extension install in a reasonable timeframe and at a reasonable size

What did you see instead?

The pecl install takes 6m 21s (on a machine that is not usually slow):

real	6m21.135s
user	5m27.342s
sys	0m30.712s

I have seen similar execution times running the pecl install as part of Docker build on Google Cloud Build. This is far and away the longest part of our entire build process.

I realise in a Docker environment it’s possible to use layer caching to reduce the impact of this, we are currently working in a hybrid environment and have some projects where we need to install onto VMs semi-regularly where a 6.5 minute build for one extension is somewhat problematic.

The generated binary is 55Mb in size:

$ ls -lh /usr/lib/php/20170718/grpc.so
-rw-r--r-- 1 root root 55M Jul 27 10:00 /usr/lib/php/20170718/grpc.so

On our VMs that’s not such an issue, but on our Docker environments again that means grpc is actually one of the single biggest contributors to our overall image size.

Anything else we should know

I found https://github.com/grpc/grpc/issues/22040#issuecomment-590601674 which suggested the binary size is largely due to debug symbols. This appears to be the case, running strip --strip-debug /usr/lib/php/20170718/grpc.so reduces the size of the binary to 5.0Mb which is much more acceptable.

However I can’t see any documentation / information on whether this is recommended and/or whether it’s possible to get pecl to build the package with the debug symbols already stripped.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 24
  • Comments: 16 (3 by maintainers)

Most upvoted comments

This is still an issue with gRPC 1.35; the grpc.so PHP extension has grown to 105 MB by now.

FYI, I have also been suffering from slow compilation. I am now using the following command to tremendously speed up the compilation process, which instructs make to use more than one processor:

MAKEFLAGS="-j $(nproc)" sudo -E pecl install grpc

I am also looking into stripping the binary during compilation now.

@stanley-cheung I’m not sure where this was fixed and therefore if I should be waiting for a release?

But FYI I am still seeing this when installing grpc-1.51.1 from pecl on a standard php:8.1-cli-alpine container. This test case on github actions took 15m 51s just for the pecl install and creates a 173.6M binary:

image

This is the Dockerfile I’m using to test that, I don’t think it’s doing anything unusual:

FROM php:8.1-cli-alpine

RUN apk add --no-cache --virtual .build-deps g++ linux-headers zlib-dev $PHPIZE_DEPS \
  && apk add --no-cache libstdc++ \
  && date \
  && export MAKEFLAGS="-j $(nproc)" \
  && time pecl install grpc \
  && date \
  && ls -lh /usr/local/lib/php/extensions/no-debug-non-zts-20210902/

Our pipeline slowed down by a factor of 10-20x when grpc was added: Screenshot from 2023-08-20 19-50-31

We’ll probably have to build an image from an already pre-built gRPC installed image, since these build times are getting out of control. pecl install grpc takes around 2200s on a 2 vCPU machine.

I’ve also this behavior on my side, compiling from source or using pecl results to a 121M file.

by using strip --strip-debug /usr/lib/php/20190902/grpc.so it reduces to 5.5M

Update: I have not found a way to pass CFLAGS without -g or LDFLAGS with -s -w to the gRPC compilation steps, so I am resorting to stripping the binary after build now with sudo strip --strip-debug /usr/lib/php/*/grpc.so.

FYI: we couldn’t use nproc because we build images in CI with Kubernetes runners and nproc returns total cores available while runners have limit, so it would lead to many zombie processes that would do nothing. I’ve hardcoded -j4 (core limit) and build went down from 50 to 20 minutes (we have complex, multi-stage build).

@cmius

You don’t really need @Wirone’s Dockerfile if you just use the below suggestions, I don’t believe @Wirone is doing anything else to decrease build times or container sizes.

Decreasing build times

You can often speed up extension build times by increasing the number of jobs make is allowed to run at once, using the -j flag. So wherever you are using pecl install you can use MAKEFLAGS="-j $(nproc)" pecl install instead and if you are using docker-php-ext-install you can use docker-php-ext-install -j$(nproc) instead.

In both of the above cases we use nproc to work out how many processing units/CPU cores are available and then get make to use that many cores. This of course assumes you want to use all possible cores.

Reducing container size

To strip all your php extensions you can just add this layer

# strip php extensions to decrease container size
RUN \
    set -eux \
    \
    && find "$(php-config --extension-dir)" -name '*.so' -type f -exec strip --strip-all {} \;

The relevant command being:

find "$(php-config --extension-dir)" -name '*.so' -type f -exec strip --strip-all {} \;

This uses find to list all the .so files inside of your php extension directory, that is what "$(php-config --extension-dir)" is for, and finally it runs strip --strip-all on those files.

Other improvements

  • Use layers in your docker, so you can utilise the docker build cache - https://docs.docker.com/build/cache/
  • Use your own internal base images that contain your standard extensions etc, so every time you build your application container you don’t also have to rebuild PHP.

N.B. If you are building multi architecture images using emulation for the different architectures this will significantly impact build times, negatively.