Skip to content

python: build with --enable-optimizations and --with-lto - #70177

Closed
acmorrow wants to merge 1 commit into
Homebrew:masterfrom
acmorrow:python39-enable-optimizations-and-lto
Closed

acmorrow wants to merge 1 commit into
Homebrew:masterfrom
acmorrow:python39-enable-optimizations-and-lto

Conversation

@acmorrow

@acmorrow acmorrow commented Feb 1, 2021 •

Copy link
Copy Markdown
Contributor
  • [Y] Have you followed the guidelines for contributing?
  • [Y] Have you checked that there aren't other open pull requests for the same formula update/change?
  • [Y] Have you built your formula locally with brew install --build-from-source <formula>, where <formula> is the name of the formula you're submitting?
  • [Y] Is your test running fine brew test <formula>, where <formula> is the name of the formula you're submitting?
  • [Y] Does your build pass brew audit --strict <formula> (after doing brew install <formula>)?

Recent python offers flags to enable PGO and LTO which hopefully results in a faster Python interpreter. This PR adds the required flags to the homebrew configure invocation. It also papers over a macOS specific issue where the configure script believes it needs llvm-ar when building --with-lto. I don't think that is actually true on macOS, as Xcode doesn't provide llvm-ar. I wasn't sure if there was an expansion like #{MacOS.sdk_path} that would be a better prefix than /usr when applying this override.

@dtrodrigues

Copy link
Copy Markdown
Member

@acmorrow

acmorrow commented Feb 1, 2021

Copy link
Copy Markdown
Contributor Author

As suggested by a colleague who knows far more about python than I do, I ran pyperformance to compare the baseline python@3.9 formula interpreter performance (compiled from source, not pulled from bottle) to that of one built from this PR. Results:

/usr/local/bin/python3.9 -m pyperformance compare ./homebrew-py39-baseline.json ./homebrew-py39-pr-70177.json 
homebrew-py39-baseline.json
===========================

Performance version: 1.0.1
Report on macOS-11.1-x86_64-i386-64bit
Number of logical CPUs: 8
Start date: 2021-02-01 16:42:05.251702
End date: 2021-02-01 17:12:14.829755

homebrew-py39-pr-70177.json
===========================

Performance version: 1.0.1
Report on macOS-11.1-x86_64-i386-64bit
Number of logical CPUs: 8
Start date: 2021-02-01 17:26:15.358418
End date: 2021-02-01 17:51:12.170954

### 2to3 ###
Mean +- std dev: 637 ms +- 35 ms -> 522 ms +- 18 ms: 1.22x faster
Significant (t=22.63)

### chameleon ###
Mean +- std dev: 19.2 ms +- 0.7 ms -> 14.5 ms +- 0.8 ms: 1.32x faster
Significant (t=31.88)

### chaos ###
Mean +- std dev: 233 ms +- 11 ms -> 160 ms +- 6 ms: 1.45x faster
Significant (t=43.79)

### crypto_pyaes ###
Mean +- std dev: 222 ms +- 17 ms -> 164 ms +- 7 ms: 1.36x faster
Significant (t=24.06)

### deltablue ###
Mean +- std dev: 15.9 ms +- 1.3 ms -> 10.8 ms +- 0.4 ms: 1.48x faster
Significant (t=29.20)

### django_template ###
Mean +- std dev: 107 ms +- 6 ms -> 76 ms +- 6 ms: 1.40x faster
Significant (t=26.72)

### dulwich_log ###
Mean +- std dev: 130 ms +- 11 ms -> 103 ms +- 6 ms: 1.26x faster
Significant (t=17.61)

### fannkuch ###
Mean +- std dev: 855 ms +- 19 ms -> 676 ms +- 11 ms: 1.26x faster
Significant (t=64.29)

### float ###
Mean +- std dev: 219 ms +- 6 ms -> 165 ms +- 6 ms: 1.32x faster
Significant (t=47.87)

### genshi_text ###
Mean +- std dev: 55.0 ms +- 2.2 ms -> 41.4 ms +- 1.5 ms: 1.33x faster
Significant (t=39.74)

### genshi_xml ###
Mean +- std dev: 119 ms +- 5 ms -> 92 ms +- 4 ms: 1.30x faster
Significant (t=34.09)

### go ###
Mean +- std dev: 521 ms +- 21 ms -> 362 ms +- 6 ms: 1.44x faster
Significant (t=56.76)

### hexiom ###
Mean +- std dev: 19.7 ms +- 0.8 ms -> 14.8 ms +- 0.9 ms: 1.33x faster
Significant (t=32.76)

### json_dumps ###
Mean +- std dev: 25.7 ms +- 1.0 ms -> 21.2 ms +- 1.2 ms: 1.21x faster
Significant (t=22.54)

### json_loads ###
Mean +- std dev: 51.9 us +- 2.6 us -> 39.4 us +- 2.3 us: 1.32x faster
Significant (t=27.56)

### logging_format ###
Mean +- std dev: 20.0 us +- 2.9 us -> 14.7 us +- 1.0 us: 1.36x faster
Significant (t=13.41)

### logging_silent ###
Mean +- std dev: 407 ns +- 32 ns -> 290 ns +- 16 ns: 1.40x faster
Significant (t=25.17)

### logging_simple ###
Mean +- std dev: 18.8 us +- 2.2 us -> 12.9 us +- 0.7 us: 1.46x faster
Significant (t=20.23)

### mako ###
Mean +- std dev: 33.8 ms +- 4.1 ms -> 24.9 ms +- 0.5 ms: 1.36x faster
Significant (t=16.64)

### meteor_contest ###
Mean +- std dev: 182 ms +- 12 ms -> 147 ms +- 4 ms: 1.24x faster
Significant (t=22.46)

### nbody ###
Mean +- std dev: 283 ms +- 18 ms -> 185 ms +- 5 ms: 1.53x faster
Significant (t=40.29)

### nqueens ###
Mean +- std dev: 194 ms +- 18 ms -> 160 ms +- 10 ms: 1.22x faster
Significant (t=13.07)

### pathlib ###
Mean +- std dev: 79.5 ms +- 5.5 ms -> 69.9 ms +- 4.6 ms: 1.14x faster
Significant (t=10.37)

### pickle ###
Mean +- std dev: 22.0 us +- 0.8 us -> 17.4 us +- 0.5 us: 1.27x faster
Significant (t=38.38)

### pickle_dict ###
Mean +- std dev: 48.0 us +- 1.6 us -> 36.6 us +- 1.2 us: 1.31x faster
Significant (t=43.32)

### pickle_list ###
Mean +- std dev: 7.54 us +- 0.40 us -> 5.54 us +- 0.15 us: 1.36x faster
Significant (t=36.64)

### pickle_pure_python ###
Mean +- std dev: 1.01 ms +- 0.05 ms -> 0.68 ms +- 0.02 ms: 1.49x faster
Significant (t=47.08)

### pidigits ###
Mean +- std dev: 264 ms +- 10 ms -> 268 ms +- 6 ms: 1.02x slower
Not significant

### pyflate ###
Mean +- std dev: 1.42 sec +- 0.04 sec -> 1.08 sec +- 0.05 sec: 1.31x faster
Significant (t=42.80)

### python_startup ###
Mean +- std dev: 29.7 ms +- 1.2 ms -> 29.2 ms +- 2.2 ms: 1.02x faster
Not significant

### python_startup_no_site ###
Mean +- std dev: 24.0 ms +- 1.2 ms -> 23.7 ms +- 1.8 ms: 1.01x faster
Not significant

### raytrace ###
Mean +- std dev: 1.11 sec +- 0.05 sec -> 0.72 sec +- 0.04 sec: 1.55x faster
Significant (t=45.60)

### regex_compile ###
Mean +- std dev: 349 ms +- 22 ms -> 253 ms +- 10 ms: 1.38x faster
Significant (t=30.42)

### regex_dna ###
Mean +- std dev: 275 ms +- 10 ms -> 247 ms +- 9 ms: 1.11x faster
Significant (t=15.74)

### regex_effbot ###
Mean +- std dev: 4.68 ms +- 0.18 ms -> 4.58 ms +- 0.33 ms: 1.02x faster
Significant (t=2.05)

### regex_v8 ###
Mean +- std dev: 37.6 ms +- 2.0 ms -> 33.1 ms +- 2.0 ms: 1.14x faster
Significant (t=12.22)

### richards ###
Mean +- std dev: 155 ms +- 7 ms -> 107 ms +- 7 ms: 1.44x faster
Significant (t=35.39)

### scimark_fft ###
Mean +- std dev: 752 ms +- 20 ms -> 530 ms +- 19 ms: 1.42x faster
Significant (t=62.60)

### scimark_lu ###
Mean +- std dev: 318 ms +- 9 ms -> 229 ms +- 6 ms: 1.39x faster
Significant (t=65.07)

### scimark_monte_carlo ###
Mean +- std dev: 218 ms +- 9 ms -> 153 ms +- 8 ms: 1.42x faster
Significant (t=41.11)

### scimark_sor ###
Mean +- std dev: 426 ms +- 18 ms -> 297 ms +- 9 ms: 1.43x faster
Significant (t=48.93)

### scimark_sparse_mat_mult ###
Mean +- std dev: 10.0 ms +- 0.7 ms -> 7.0 ms +- 0.4 ms: 1.43x faster
Significant (t=28.82)

### spectral_norm ###
Mean +- std dev: 309 ms +- 10 ms -> 213 ms +- 10 ms: 1.45x faster
Significant (t=52.20)

### sqlalchemy_declarative ###
Mean +- std dev: 271 ms +- 14 ms -> 233 ms +- 22 ms: 1.17x faster
Significant (t=11.61)

### sqlalchemy_imperative ###
Mean +- std dev: 49.8 ms +- 2.5 ms -> 42.3 ms +- 3.6 ms: 1.18x faster
Significant (t=13.26)

### sqlite_synth ###
Mean +- std dev: 4.91 us +- 0.24 us -> 4.25 us +- 0.29 us: 1.16x faster
Significant (t=13.57)

### sympy_expand ###
Mean +- std dev: 926 ms +- 42 ms -> 704 ms +- 29 ms: 1.31x faster
Significant (t=33.60)

### sympy_integrate ###
Mean +- std dev: 43.1 ms +- 2.5 ms -> 36.4 ms +- 4.3 ms: 1.19x faster
Significant (t=10.48)

### sympy_str ###
Mean +- std dev: 593 ms +- 21 ms -> 474 ms +- 28 ms: 1.25x faster
Significant (t=26.42)

### sympy_sum ###
Mean +- std dev: 385 ms +- 20 ms -> 310 ms +- 19 ms: 1.24x faster
Significant (t=20.90)

### telco ###
Mean +- std dev: 12.1 ms +- 0.6 ms -> 9.1 ms +- 0.7 ms: 1.33x faster
Significant (t=25.63)

### unpack_sequence ###
Mean +- std dev: 101 ns +- 4 ns -> 90 ns +- 5 ns: 1.12x faster
Significant (t=13.93)

### unpickle ###
Mean +- std dev: 27.9 us +- 1.3 us -> 21.5 us +- 1.2 us: 1.30x faster
Significant (t=27.73)

### unpickle_list ###
Mean +- std dev: 7.65 us +- 0.29 us -> 5.68 us +- 0.43 us: 1.35x faster
Significant (t=29.43)

### unpickle_pure_python ###
Mean +- std dev: 709 us +- 39 us -> 491 us +- 16 us: 1.44x faster
Significant (t=40.12)

### xml_etree_generate ###
Mean +- std dev: 163 ms +- 4 ms -> 132 ms +- 9 ms: 1.24x faster
Significant (t=25.11)

### xml_etree_iterparse ###
Mean +- std dev: 191 ms +- 7 ms -> 159 ms +- 6 ms: 1.20x faster
Significant (t=26.51)

### xml_etree_parse ###
Mean +- std dev: 261 ms +- 9 ms -> 238 ms +- 13 ms: 1.10x faster
Significant (t=11.37)

### xml_etree_process ###
Mean +- std dev: 130 ms +- 3 ms -> 105 ms +- 8 ms: 1.23x faster
Significant (t=22.03)

Skipped 1 benchmarks only in homebrew-py39-pr-70177.json: tornado_http

@acmorrow

acmorrow commented Feb 2, 2021

Copy link
Copy Markdown
Contributor Author

Some previous discussion regarding those flags:
#45337 (comment)

Most of the relevant discussion in this link seems to be about the fact that the official binaries don't do this, so homebrew shouldn't either. If that is homebrew's decided policy then it would make sense to close this PR.

https://mail.python.org/archives/list/python-dev@python.org/thread/EILILECNTLTW4VCBCPW37R4QRU7ZBDEU/#VUA2AG6J2QG2GK52DZ4VKZMRUTKJ4DWO

However, this provides a good explanation for why the official python binaries don't do this: they want one binary package for all macOS back to a fairly old version. But if I understand how homebrew works, then that isn't as much an issue for this change: bottles are OS version specific, and otherwise you build on local. So it sounds as if that issue could be sidestepped.

If there are concerns about enabling these flags on older platforms or older versions of Xcode, perhaps we could make their application conditional on Xcode and OS version? Then at least newer platforms like Big Sur with Xcode 12 could get the better performance. If that would make this change more likely to be accepted I'm happy to make that change. It would be helpful if someone could point me to some example formulae that also introduce such conditionals.

Note that at least the Ubuntu 20.04 debian packaging of python-3.8 does use LTO and PGO, but does so manually, rather than with --enable-optimizations and --with-lto. I'm not sure if that is an argument for or against using accepting this change, but I found it interesting.

@dtrodrigues

Copy link
Copy Markdown
Member

@Homebrew/core thoughts here? Since we support back to 10.14 with individual bottles vs a single installer on 10.9 for the python.org installer, would these flags make sense in Homebrew at this point?

@carlocab

carlocab commented Feb 2, 2021 •

Copy link
Copy Markdown
Member

Yes, it does seem like upstream's reasons for not enabling these flags don't apply here. It also seems like we have some pretty compelling reasons (56 of them, in fact) to enable these flags. It would be a shame not to take advantage of the fact that our bottling infrastructure is more specialised than what python.org uses to produce their installer.

There is this cryptic but seemingly related comment in the linked discussion I want to make sense of, however: #45337 (comment)

@acmorrow

acmorrow commented Feb 2, 2021

Copy link
Copy Markdown
Contributor Author

There is this cryptic but seemingly related comment in the linked discussion I want to make sense of, however: #45337 (comment)

That is an interesting comment. Personally, my motivation here is that I use homebrew python to drive SCons for the mongodb build on macOS. SCons has its charms but speed is not currently among them, so anything that may accelerate it is valuable to me. Experimentation showed that enabling these flags in our local linux builds of python had significant performance advantages for certain SCons builds, and it seemed worthwhile to take the result of those findings over to homebrew, if possible. "Production" means different things to different people: I at least consider homebrew python to be a production level component of my macOS development environment.

@carlocab

carlocab commented Feb 2, 2021

Copy link
Copy Markdown
Member

Another benefit of these flags: it appears to have shortened running time of the Mojave CI node by about 5 hours. (Compare: before, after)

Of course, this is hardly a scientific comparison, but I do think it's not unreasonable to claim that these flags will help shorten CI queueing times.

@SMillerDev

Copy link
Copy Markdown
Member

If the only reason is compatibility I am fine with deviating (with some documentation) from the upstream default.

Comment thread Formula/python@3.9.rb Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with those changes.

Comment thread Formula/python@3.9.rb Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, on the other hand, needs to be reported to the Python developers. If the build fails without it, it's a Python bug. Please add a comment above that line with the Python bug link, so we can check whether it's safe to remove in the future.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll get that reported to them and will add the bug link here. Meanwhile if we are going to continue with this workaround until the bug is fixed, is the /usr/bin portion here OK? Or is there an expansion I should be using to reach into the CLI tools? Using xcrun? I don't know enough about homebrew to know.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/usr/bin is fine (but is a full path necessary, though?)

@carlocab carlocab Feb 2, 2021 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLVM_AR=ar seems to work fine for me locally. Nope, this doesn't seem to be true. I think I was mistaken about my ./configure flags when I wrote this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My recollection is that I did need it and your experiment seems to confirm. Do you want me to double check? Or OK to move forward with the explicit path without further work?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed https://bugs.python.org/issue43109. I'll get the link added to the PR, along with the comment changes requested elsewhere.

@acmorrow

acmorrow commented Feb 2, 2021

Copy link
Copy Markdown
Contributor Author

If the only reason is compatibility I am fine with deviating (with some documentation) from the upstream default.

What form should this documentation take, and where should I add it?

@SMillerDev

Copy link
Copy Markdown
Member

What form should this documentation take, and where should I add it?

A comment above the configure flags along the lines of "deviating from default because we don't need backwards compatibility" is good enough iyam.

@acmorrow
acmorrow force-pushed the python39-enable-optimizations-and-lto branch from 20ce264 to f60c3df Compare February 2, 2021 23:30
@acmorrow

acmorrow commented Feb 2, 2021

Copy link
Copy Markdown
Contributor Author

Updated with requested comment changes for --enable-optimizations --with-lto and for the LLVM_AR workaround, including a link to the python bug.

@carlocab
carlocab force-pushed the python39-enable-optimizations-and-lto branch from f60c3df to 840ab28 Compare February 3, 2021 06:27
@carlocab

carlocab commented Feb 3, 2021

Copy link
Copy Markdown
Member

I just merged #68528, so I rebased this to incorporate those changes. Also, included a revision bump and edited a comment a bit for readability.

This is done using the configure flags

    --enable-optimizations and --with-lto
@carlocab
carlocab force-pushed the python39-enable-optimizations-and-lto branch from 840ab28 to e4bd2e9 Compare February 3, 2021 06:45
@acmorrow

acmorrow commented Feb 3, 2021

Copy link
Copy Markdown
Contributor Author

@carlocab - Anything else you need from me on this?

@carlocab

carlocab commented Feb 4, 2021

Copy link
Copy Markdown
Member

@acmorrow Just your patience as we wait for CI to finish 馃槃

@carlocab

carlocab commented Feb 4, 2021 •

Copy link
Copy Markdown
Member

ARM:

Error: 2 failed steps!
brew test --retry --verbose emscripten
brew test --retry --verbose tree-sitter

Big Sur:

Error: 9 failed steps!
brew test --retry --verbose asuka
brew test --retry --verbose creduce
brew test --retry --verbose geant4
brew test --retry --verbose glyr
brew test --retry --verbose hyperkit
brew install llvm@8
brew test --retry --verbose qxmpp
brew test --retry --verbose treefrog
brew test --retry --verbose wasm-pack

Catalina:

Error: 6 failed steps!
brew test --retry --verbose glyr
brew test --retry --verbose hyperkit
brew test --retry --verbose massdns
brew test --retry --verbose qxmpp
brew test --retry --verbose treefrog
brew test --retry --verbose wasm-pack

Mojave:

Error: 6 failed steps!
brew test --retry --verbose cp2k
brew test --retry --verbose hyperkit
brew test --retry --verbose nwchem
brew test --retry --verbose swift
brew test --retry --verbose treefrog
brew test --retry --verbose wasm-pack

Most of these are documented in #66450. The ones that don't look spurious (e.g. llvm@8 is an out-of-memory error) or unrelated.

@BrewTestBot

Copy link
Copy Markdown
Contributor

:shipit: @carlocab has triggered a merge.

@acmorrow

acmorrow commented Feb 4, 2021

Copy link
Copy Markdown
Contributor Author

@acmorrow Just your patience as we wait for CI to finish 馃槃

Hah, sure thing. I'm very familiar with that kind of waiting. Looks from your recent comment though that the results are as expected.

@acmorrow

acmorrow commented Feb 8, 2021

Copy link
Copy Markdown
Contributor Author

@carlocab, @fxcoudert, and @dtrodrigues - Thanks for all of your help getting this merged. Homebrew is a great project and a critical part of my development environment. I rely on it daily and it was fun to have an opportunity to participate in its development. I hope I've made python just a little faster for all of your users.

PS @carlocab - I've noticed that new bottles don't seem to have gone out that incorporate this change. Not sure if that is just normal latency in the process or something else.

@carlocab

carlocab commented Feb 8, 2021

Copy link
Copy Markdown
Member

Hmm, that's weird. The new bottles should've incorporated that change. How do you verify whether Python was built with the optimisation flags?

@acmorrow

acmorrow commented Feb 8, 2021

Copy link
Copy Markdown
Contributor Author

Ah, I think I failed to clean up properly after putting together the PR and was pinned to older state. Sorry for the false alarm.

To answer your question about how to see how it was built:

/usr/local/Cellar/python@3.9/3.9.1_8/bin/python3 
Python 3.9.1 (default, Feb  3 2021, 07:38:02)
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pprint
>>> import sysconfig
>>> pprint.pprint(sysconfig.get_config_vars()['CONFIG_ARGS'])
("'--prefix=/usr/local/opt/python@3.9' '--enable-ipv6' "
 "'--datarootdir=/usr/local/opt/python@3.9/share' "
 "'--datadir=/usr/local/opt/python@3.9/share' "
 "'--enable-loadable-sqlite-extensions' "
 "'--with-openssl=/usr/local/opt/openssl@1.1' '--with-dbmliborder=gdbm:ndbm' "
 "'--enable-optimizations' '--with-lto' "
 "'--enable-framework=/usr/local/opt/python@3.9/Frameworks' '--with-dtrace' "
 "'LLVM_AR=/usr/bin/ar' 'MACOSX_DEPLOYMENT_TARGET=11' "
 "'--with-tcltk-includes=-I/usr/local/opt/tcl-tk/include' "
 "'--with-tcltk-libs=-L/usr/local/opt/tcl-tk/lib -ltcl8.6 -ltk8.6' "
 "'CFLAGS=-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' "
 "'CFLAGS_NODIST=-I/usr/local/include' 'LDFLAGS=-isysroot "
 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' "
 "'LDFLAGS_NODIST=-L/usr/local/lib' 'CPPFLAGS=-I/usr/local/include' 'CC=clang' "
 "'PKG_CONFIG_PATH=/usr/local/opt/openssl@1.1/lib/pkgconfig:/usr/local/opt/readline/lib/pkgconfig:/usr/local/opt/sqlite/lib/pkgconfig:/usr/local/opt/tcl-tk/lib/pkgconfig:/usr/local/opt/xz/lib/pkgconfig' "
 "'PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig/11'")

@BrewTestBot BrewTestBot added the outdated PR was locked due to age label Mar 11, 2021
@Homebrew Homebrew locked as resolved and limited conversation to collaborators Mar 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

outdated PR was locked due to age

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants