-
Notifications
You must be signed in to change notification settings - Fork 75
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
Add GCC to runtime dependencies #127
Conversation
Ruby 2.6.0 ships with a JIT (just in time compiler) that utilizes GCC (or any other `cc` such as clang) at runtime. This version of Ruby will be released on December 25th, 2018 and Heroku needs to provide support for this feature out of the box. ## Background The design alternatives can be found in this document https://docs.google.com/document/d/1q5yEpRddUU2JPynESmffI_BpLDW4XaBgO7iyJE-mwio/edit?usp=sharing ## This PR This PR adds the `gcc` package to Heroku-16 and Heroku-18 runtime images. ## Size cost Adding the `gcc` package increases the runtime stack image by about 84mb: Before adding `gcc` ``` -----> Size breakdown... # ... ubuntu:18.04 85.8MB heroku/heroku:18 428MB heroku/heroku:18-build 805MB ``` After adding `gcc` ``` -----> Size breakdown... # ... ubuntu:18.04 85.8MB heroku/heroku:18 512MB heroku/heroku:18-build 805MB ``` It looks like this works with only `gcc`, however if we end up needing `build-essential` then the cost would increase by 64MB to 576MB. ## Testing JIT on the stack images testing On a currently running Heroku-18 dyno with Ruby 2.6: ``` $ hs run bash -a infinite-everglades-58502 ~ $ ruby -v ruby 2.6.0preview3 (2018-11-06 trunk 65578) [x86_64-linux] ~ $ ruby -e "def a; end; def b; end; a; a; a; a; a; a; a; puts 'done'" --jit --jit-verbose=1 MJIT: Error in execv: /usr/bin/gcc MJIT warning: Making precompiled header failed on compilation. Stopping MJIT worker... done ``` > Note: The warnings in the output indicate gcc could not be found On a local docker image with `gcc` available: ``` $ bin/build.sh heroku-18 heroku/heroku:18 heroku/heroku:18-build # ... $ docker run -i -t heroku/heroku:18 /bin/bash root@4437f834d502:/# mkdir -p vendor/ruby-2.6.0 root@4437f834d502:/# cd vendor/ruby-2.6.0 root@4437f834d502:/vendor/ruby-2.6.0# curl https://heroku-buildpack-ruby.s3.amazonaws.com/heroku-18/ruby-2.6.0.tgz -s -o - | tar zxf - root@4437f834d502:/vendor/ruby-2.6.0# bin/ruby -e "def a; end; def b; end; a; a; a; a; a; a; a; puts 'done'" --jit --jit-verbose=1 done Successful MJIT finish ``` > Note: The output indicates that MJIT was successful For more output you can also apply `--jit-wait` which will force mjit to run synchronously (instead of in a background thread). cc/ @hone @hunterloftis
Also looks like you can get a return status code by trying to interact with MJIT internals if it's not active. On current Heroku 18 dyno:
|
For the record, @schneems and I have been discussing possibly using musl instead of GCC to reduce impact.
Our thinking now is therefore that the slug size reduction isn't worth the fairly large effort it would be to switch to musl instead of gcc. |
Thanks 🙏 ! When will this be released so I can test and verify everything works in prod? |
Next week. |
I shipped it this morning. https://devcenter.heroku.com/changelog-items/1531 |
Confirm that it works! Thanks ❤️
|
GCC was added to our run images back in #127 in order to support Ruby 2.6's then new MJIT feature: https://www.ruby-lang.org/en/news/2018/12/25/ruby-2-6-0-released/ However, since then: - The Ruby MJIT feature hasn't really resulted in significant performance benefits for real world use-cases like a Rails app. - Ruby's MJIT has since been superseded by YJIT, which is faster and doesn't need GCC at runtime: https://shopify.engineering/yjit-just-in-time-compiler-cruby https://shopify.engineering/ruby-yjit-is-production-ready - The image size impact of including build tools in our run images has increased considerably (#127 quoted it as 84 MB, but measuring now it's 203 MB). - In a CNB world, image size is much more of a concern than in the S3 `.img` + slug model, so we need to be more selective over what packages we include. As such, this removes `gcc`, `make` and `libc6-dev` from the run image for a 203 MB saving (they are still present in the build image, hence zero changes to `installed-packages-*.txt` for that image). Richard (Ruby owner) has confirmed he's fine with this change. Note: I'm intentionally not adding `binutils` back (which was a transitive dependency), since its 15 MB cost is not worth it for the ~once a year platform operator debugging use-case. Before: ``` -----> Size breakdown... heroku/heroku:24 661MB heroku/heroku:24-build 1.13GB ``` After: ``` -----> Size breakdown... heroku/heroku:24 458MB heroku/heroku:24-build 1.13GB ``` Towards #266. GUS-W-15159536.
Ruby 2.6.0 ships with a JIT (just in time compiler) that utilizes GCC (or any other
cc
such as clang) at runtime. This version of Ruby will be released on December 25th, 2018 and Heroku needs to provide support for this feature out of the box.Background
The design alternatives can be found in this document
https://docs.google.com/document/d/1q5yEpRddUU2JPynESmffI_BpLDW4XaBgO7iyJE-mwio/edit?usp=sharing
This PR
This PR adds the
gcc
package to Heroku-16 and Heroku-18 runtime images.Size cost
Adding the
gcc
package increases the runtime stack image by about 84mb:Before adding
gcc
After adding
gcc
It looks like this works with only
gcc
, however if we end up needingbuild-essential
then the cost would increase by 64MB to 576MB.Testing JIT on the stack images testing
On a currently running Heroku-18 dyno with Ruby 2.6:
On a local docker image with
gcc
available:For more output you can also apply
--jit-wait
which will force mjit to run synchronously (instead of in a background thread).cc/ @hone @hunterloftis