├── .version ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── benchmarks ├── 3.5.4 │ ├── py354_lto_pgo.json │ └── py354_nonopt.json └── 3.6.3 │ ├── python3.6.3-optimized_0.json │ ├── python3.6.3-optimized_2.json │ ├── python3.6.3-optimized_3.json │ ├── python3.6.3-optimized_4.json │ ├── python3.6.3-optimized_5.json │ ├── python3.6.3-optimized_6.json │ ├── python3.6.3-optimized_7.json │ ├── python3.6.3-optimized_8.json │ ├── python3.6.3-optimized_9.json │ ├── python3.6.3-unoptimized_0.json │ ├── python3.6.3-unoptimized_1.json │ ├── python3.6.3-unoptimized_2.json │ ├── python3.6.3-unoptimized_3.json │ ├── python3.6.3-unoptimized_4.json │ ├── python3.6.3-unoptimized_5.json │ ├── python3.6.3-unoptimized_6.json │ ├── python3.6.3-unoptimized_7.json │ ├── python3.6.3-unoptimized_8.json │ └── python3.6.3-unoptimized_9.json ├── examples └── squid.conf ├── gnupg ├── crls.d │ └── DIR.txt ├── pubring.gpg ├── pubring.gpg~ ├── tofu.db └── trustdb.gpg ├── init-functions ├── ipython_config.py └── revsys_Python-Optimized-Images_and_Benchmark-Data.md /.version: -------------------------------------------------------------------------------- 1 | # vim: ft=sh 2 | 3 | 4 | export IMAGE_VERSION=${PYTHON_VERSION}-wee 5 | export IMAGE=revolutionsystems/python 6 | 7 | export MAJOR_VERSION="$( echo ${PYTHON_VERSION} | cut -d"." -f1 )" 8 | 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # vim: ft=dockerfile ts=4 sw=4 expandtab 2 | ############################################################################### 3 | # 4 | # Multi-stage Python 3.x build 5 | # 6 | # build-time environment variables: 7 | # LTO=1 . enable link-time-optimizations 8 | # OPTIMAL=1 . enable profile-guided-optimizations (PGO) 9 | # PYTHON_VERSION=3.5.3 10 | # 11 | # ** NOTE **: 12 | # . LTO requires PGO 13 | # . ensure both variables are unset for typical builds 14 | # 15 | # building: 16 | # make build-image . run docker build 17 | # make build-push . push image to repository 18 | # make image . build + push 19 | # 20 | # Stages: 21 | # runtime <- debian-base-amd64:0.2 22 | # common runtime packages go here 23 | # build-setup <- runtime 24 | # dev packages, tools, utilities, etc. go here 25 | # builder <- build-setup 26 | # ./configure && make && make install 27 | # post-build <- builder 28 | # install any common python modules here 29 | # FINAL <- runtime 30 | # pip package installation goes here + ENTRYPOINT 31 | # 32 | ############################################################################### 33 | 34 | #FROM gcr.io/google-containers/debian-base-amd64:v2.0.0 as runtime 35 | FROM debian:buster-slim as base 36 | 37 | ENV PATH /usr/local/bin:$PATH 38 | 39 | # http://bugs.python.org/issue19846 40 | # > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK. 41 | 42 | ENV LANG C.UTF-8 43 | 44 | COPY ./init-functions /lib/lsb/ 45 | 46 | RUN set -ex \ 47 | && apt update \ 48 | && apt -y upgrade \ 49 | && apt-mark unhold apt libcap2 libsemanage1 passwd \ 50 | && apt-get install --no-install-recommends -qq -y ca-certificates libsqlite3-0 zlib1g libexpat1 bash procps less libbz2-1.0 netcat-openbsd git binutils \ 51 | && find /usr -type f -name "*.so" -exec strip --strip-unneeded {} + \ 52 | && apt-get remove -qq --allow-remove-essential --purge -y -qq \ 53 | binutils e2fsprogs e2fslibs libx11-6 libx11-data \ 54 | && find /var/lib/apt/lists \ 55 | /usr/share/man \ 56 | /usr/share/doc \ 57 | /var/log \ 58 | -type f -exec rm -f {} + \ 59 | && rm -rf /root/.gnupg \ 60 | && mkdir -p /root/.gnupg \ 61 | && chmod 700 /root/.gnupg 62 | 63 | LABEL stage RUNTIME 64 | 65 | ############################################################################### 66 | FROM scratch as runtime 67 | 68 | ENV PATH /usr/local/bin:$PATH 69 | 70 | # http://bugs.python.org/issue19846 71 | # > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK. 72 | ENV LANG C.UTF-8 73 | 74 | COPY ./init-functions /lib/lsb/ 75 | 76 | COPY --from=base / / 77 | 78 | ############################################################################### 79 | FROM alpine as source-download 80 | 81 | ARG PYTHON_VERSION 82 | ENV PYTHON_VERSION ${PYTHON_VERSION} 83 | 84 | ENV SRCDIR /python 85 | RUN apk add curl 86 | RUN mkdir -p /python /build \ 87 | && tar -xJC ${SRCDIR} --strip-components=1 -f <( curl -sL "https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz" ) 88 | 89 | 90 | ############################################################################### 91 | FROM runtime as build-setup 92 | 93 | WORKDIR /python 94 | 95 | RUN apt-get update 96 | RUN apt-get -y install --no-install-recommends \ 97 | libsqlite3-dev zlib1g-dev libexpat1-dev \ 98 | libssl-dev xz-utils dpkg-dev binutils libbz2-dev \ 99 | libreadline-dev libffi-dev libncurses5 \ 100 | libncurses5-dev libncursesw5 openssl \ 101 | gcc g++ make autoconf libtool \ 102 | dpkg-dev 103 | 104 | LABEL stage BUILD-SETUP 105 | 106 | ############################################################################### 107 | FROM build-setup as builder 108 | 109 | ARG BUILD_ARGS 110 | ARG PYTHON_VERSION 111 | ENV LANG C.UTF-8 112 | 113 | ENV CFLAGS -I/usr/include/openssl 114 | 115 | WORKDIR /build 116 | 117 | COPY --from=source-download /python /python 118 | 119 | RUN set -ex \ 120 | && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ 121 | && [ $(( ` echo $PYTHON_VERSION | cut -d"." -f1 ` )) -lt 3 ] && BUILD_ARGS="" \ 122 | ; ../python/configure \ 123 | --build="$gnuArch" \ 124 | --enable-loadable-sqlite-extensions \ 125 | --enable-shared \ 126 | --with-system-expat \ 127 | --with-system-ffi \ 128 | --without-ensurepip ${BUILD_ARGS} 129 | 130 | RUN make -j $(( 1 * $( egrep '^processor[[:space:]]+:' /proc/cpuinfo | wc -l ) )) \ 131 | && make install 132 | 133 | RUN set -ex \ 134 | find /usr/local -type f -name "*.so" -exec strip --strip-unneeded {} + \ 135 | & ldconfig \ 136 | & find /usr/local -depth \ 137 | \( \ 138 | \( -type d -a \( -name test -o -name tests -o -name __pycache__ \) \) \ 139 | -o \ 140 | \( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \ 141 | -o \ 142 | \( -name "idle*" \) \ 143 | \) -exec rm -rf '{}' + \ 144 | && find /var/lib/apt/lists \ 145 | /usr/share/man \ 146 | /usr/share/doc \ 147 | /var/log \ 148 | -type f -exec rm -f {} + 149 | 150 | # make some useful symlinks that are expected to exist 151 | RUN ["/bin/bash", "-c", "if [[ $( echo ${PYTHON_VERSION} | cut -d'.' -f1 ) == '3' ]]; then cd /usr/local/bin && ln -sf pydoc3 pydoc && ln -sf python3 python && ln -sf python3-config python-config; fi"] 152 | 153 | LABEL stage BUILDER 154 | LABEL version ${PYTHON_VERSION} 155 | 156 | ############################################################################### 157 | FROM builder as post-build 158 | 159 | # if this is called "PIP_VERSION", pip explodes with "ValueError: invalid truth value ''" 160 | ENV PYTHON_PIP_VERSION 22.2.2 161 | 162 | 163 | ADD https://bootstrap.pypa.io/get-pip.py . 164 | 165 | RUN set -ex; ldconfig 166 | RUN set -ex; python get-pip.py \ 167 | --disable-pip-version-check \ 168 | --no-cache-dir; \ 169 | pip --version 170 | # "pip==$PYTHON_PIP_VERSION"; 171 | 172 | 173 | RUN set -ex; \ 174 | find /usr/local -depth \ 175 | \( \ 176 | \( -type d -a \( -name test -o -name tests -o -name __pycache__ \) \) \ 177 | -o \ 178 | \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name '*.exe' \) \) \ 179 | \) -exec rm -rf '{}' +; 180 | 181 | RUN set -ex; \ 182 | find /usr/share/ 183 | RUN rm -rf /root/.cache 184 | 185 | ARG PYTHON_VERSION 186 | LABEL stage POST-BUILD 187 | LABEL version ${PYTHON_VERSION} 188 | 189 | ############################################################################### 190 | FROM runtime as release 191 | 192 | COPY --from=post-build /usr/local /usr/local 193 | COPY --from=post-build /root/* /root/ 194 | 195 | RUN /sbin/ldconfig 196 | 197 | LABEL stage FINAL 198 | ARG PYTHON_VERSION 199 | LABEL version ${PYTHON_VERSION} 200 | 201 | CMD ["python"] 202 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Revolution Systems, LLC 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: image push-image build-image optimal buil-builder 2 | 3 | include .version 4 | 5 | PYTHON_VERSION ?= 3.6.2 6 | 7 | TAG = ${PYTHON_VERSION}-wee 8 | 9 | IMAGE_TAG = ${IMAGE}:${TAG}${TAG_SUFFIX} 10 | LATEST = ${IMAGE}:latest 11 | 12 | 13 | PLATFORMS ?= linux/amd64 14 | 15 | TARGETS := --platform ${PLATFORMS} 16 | 17 | ifdef PKG_PROXY 18 | PROXY_ARGS := --build-arg=http_proxy=${PKG_PROXY} --build-arg=https_proxy=${PKG_PROXY} 19 | else 20 | PROXY_ARGS := 21 | endif 22 | 23 | ifdef PIP_PROXY_HOST 24 | PIP_PROXY_ARGS := --add-host=pypi.python.org=${PIP_PROXY_HOST} 25 | else 26 | PIP_PROXY_ARGS := 27 | endif 28 | 29 | 30 | ifdef OPTIMAL 31 | BUILD_ARGS += --enable-optimizations 32 | TAG := ${TAG}-optimized 33 | endif 34 | 35 | ifdef LTO 36 | BUILD_ARGS += --with-lto 37 | TAG := ${TAG}-lto 38 | endif 39 | 40 | 41 | ifndef DOCKERFILE 42 | DOCKERFILE := ./Dockerfile 43 | endif 44 | 45 | TAG_SUFFIX ?= 46 | 47 | build-builder: 48 | docker buildx build ${PROXY_ARGS} ${TARGETS} --cache-from=type=registry,ref=${LATEST} --cache-to=type=registry,ref=${LATEST},mode=max -f ${DOCKERFILE} --build-arg=PYTHON_VERSION=${PYTHON_VERSION} --build-arg=BUILD_ARGS="${BUILD_ARGS}" --target builder -t ${IMAGE_TAG} . 49 | 50 | build-image: build-builder 51 | @echo building ${IMAGE_TAG} 52 | docker buildx build ${PROXY_ARGS} ${TARGETS} --cache-from=type=registry,ref=${LATEST} -f ${DOCKERFILE} --build-arg=PYTHON_VERSION=${PYTHON_VERSION} --build-arg=BUILD_ARGS="${BUILD_ARGS}" --load -t ${IMAGE_TAG} . 53 | 54 | push-image: 55 | @echo pushing ${IMAGE_TAG} 56 | docker buildx build ${PROXY_ARGS} ${TARGETS} --cache-from=type=registry,ref=${LATEST} -f ${DOCKERFILE} --build-arg=PYTHON_VERSION=${PYTHON_VERSION} --build-arg=BUILD_ARGS="${BUILD_ARGS}" --push -t ${IMAGE_TAG} . 57 | 58 | image: push-image 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # RevSys Python Builds 3 | 4 | ## What’s wrong with stock Python images? 5 | 6 | The main image variety is too big when the user is operating in a context 7 | that involves multiple private registries. 8 | 9 | It’s base layer is an Ubuntu image (with a few exceptions, this 10 | provides an as-installed-by-Ubuntu environment) which is, essentially, what 11 | one gets with a base minimal install of Ubuntu 16.04. 12 | 13 | The [alpine](https://alpinelinux.org/) images are delightfully wee. But, alas, 14 | [musl](https://www.musl-libc.org/). It is awesome. I mean, it *exists*. If you 15 | don’t have build problems with your modules that have a C or C++ component, 16 | use it. It is small, fast and well vetted. 17 | 18 | **Note**: There are workarounds for *some* of the more common non-pure-python 19 | module build failures but usually involve much shenaniganry that I have neither the time 20 | nor inclination to pursue. 21 | 22 | The **slim** images are similar to the ubuntu images. Rather than an Ubuntu base 23 | image, they use a Debian 8 base. 24 | 25 | | REPOSITORY | TAG | SIZE | 26 | | ----------- | ---------------------- | ----- | 27 | | python | 3.6.3 | 690MB | 28 | | python | 3.6-slim | 201MB | 29 | | python | 3.6.3-alpine | 89MB | 30 | | registry.gitlab.com/revsys/docker-builds/python | 3.6.3-wee | 153MB | 31 | 32 | We settled on a Debian 8 image provided by google’s GCE service. It is smaller 33 | by way of excluding systemd-related bits while building the initial bootstrap 34 | chroot. 35 | 36 | The resulting images are smaller than -slim but larger than alpine. 37 | 38 | ## Optimized Python images 39 | 40 | Three builds are available per Python version: 41 | 42 | * :wee - stock build without compile or link-time optimizations enabled 43 | * :wee-optimized - build with profile-guided optimization flag 44 | * :wee-optimized-lto - build with PGO + Link-Time-Optimization flags 45 | 46 | 47 | | REPOSITORY | TAG | SIZE | 48 | | ----------- | ---------------------- | ----- | 49 | | revolutionsystems/python | 3.5.3-wee-optimized-lto | 164MB | 50 | | revolutionsystems/python | 3.5.3-wee-optimized | 164MB | 51 | | revolutionsystems/python | 3.5.3-wee | 158MB | 52 | 53 | ## Building 54 | 55 | The registry is included from /.versions 56 | 57 | make targets: 58 | 59 | * build-image: docker build... 60 | * push-image: docker push ... 61 | * image: runs build-image then push-image 62 | 63 | ### stock build 64 | 65 | ``` 66 | 67 | :# LTO= OPTIMAL= PYTHON_VERSION=3.5.3 make image 68 | 69 | ``` 70 | 71 | ### PGO optimizations 72 | 73 | ``` 74 | 75 | :# LTO= OPTIMAL=1 PYTHON_VERSION=3.5.1 make image 76 | 77 | ``` 78 | 79 | ### LTO Optimizations 80 | 81 | ``` 82 | 83 | :# LTO=1 OPTIMAL=1 PYTHON_VERSION=3.6.3 make image 84 | 85 | ``` 86 | 87 | ### Python 2 builds 88 | 89 | The build arguments have stayed fairly consistent at least since Python 2.6. 90 | The build will not fail if the Python 3-specific environment variables remain 91 | set; however, the image will get a misleading tag. For Python 2 builds, use: 92 | 93 | ``` 94 | 95 | :# LTO= OPTIMAL= PYTHON_VERSION=2.7.13 make image 96 | 97 | ``` 98 | 99 | ## Build Optimization 100 | 101 | Build times are radically reduced by using a local proxy. You can find an 102 | example squid 3/4 configuration in ./examples. 103 | 104 | After setting the `cache_dir` path the command, `squid -f /path/to/config -N -z` 105 | to initialize the cache filesystem storage. 106 | 107 | To kick off the cache service: `squid -f /path/to/config -N -a 3128`. 108 | 109 | Once the proxy is running, set the environment variable `export PKG_PROXY=http://10.129.1.1:3128`. 110 | Docker transparently passes the `HTTP_PROXY` and `HTTPS_PROXY` environment variables 111 | into each build container if they are configured with the `--build-args` flag. 112 | 113 | The transparency is significant in that whether or not your build is leveraging 114 | a proxy will not affect the validity of the build host’s cache. 115 | 116 | 117 | ## Origin Notes 118 | 119 | Based in large part on the contents of [the Docker Library Python repository](https://github.com/docker-library/python). 120 | 121 | --- 122 | 123 | 1. uses [GNU make](https://www.gnu.org/software/make/) and environment variables to set options 124 | 1. `LTO`: if present and not empty, activate link-time-optimization functionality 125 | 1. `OPTIMAL`: if present and not empty, activate profiler-guided-optimization functionality 126 | 1. `PYTHON_VERSION’: e.g.: 3.6.3, 3.5.1 127 | 1. Does not include [TCL/TK](https://www.tcl.tk/about/uses.html). This is on purpose. If you neeed IDLE or 128 | pexpect you’ll need to customize the Dockerfile and rebuild. 129 | 1. Uses multi-layer Dockerfile syntax 130 | 131 | --- 132 | 133 | Keep in touch! 134 | -------------- 135 | 136 | If you have a question about this project, please open a GitHub issue. If you love us and want to keep track of our goings-on, here's where you can find us online: 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /examples/squid.conf: -------------------------------------------------------------------------------- 1 | 2 | acl localhost src 127.0.0.1/32 3 | acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 4 | acl localnet src 10.0.0.0/8 # RFC1918 possible internal network 5 | acl localnet src 172.16.0.0/12 # RFC1918 possible internal network 6 | acl localnet src 192.168.0.0/16 # RFC1918 possible internal network 7 | acl SSL_ports port 443 8 | acl Safe_ports port 80 # http 9 | acl Safe_ports port 21 # ftp 10 | acl Safe_ports port 443 # https 11 | acl Safe_ports port 70 # gopher 12 | acl Safe_ports port 210 # wais 13 | acl Safe_ports port 1025-65535 # unregistered ports 14 | acl Safe_ports port 280 # http-mgmt 15 | acl Safe_ports port 488 # gss-http 16 | acl Safe_ports port 591 # filemaker 17 | acl Safe_ports port 777 # multiling http 18 | acl CONNECT method CONNECT 19 | http_access allow manager localhost 20 | http_access deny manager 21 | http_access deny !Safe_ports 22 | http_access deny CONNECT !SSL_ports 23 | http_access allow localnet 24 | http_access allow localhost 25 | http_access deny all 26 | http_port 3128 27 | maximum_object_size 1024 MB 28 | # SET to location of on-disk cache 29 | cache_dir aufs 5000 24 256 max-size=10240000 min-size=10 30 | coredump_dir 31 | refresh_pattern ^ftp: 1440 20% 10080 32 | refresh_pattern ^gopher: 1440 0% 1440 33 | refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 34 | refresh_pattern (Release|Packages(.gz)*)$ 0 20% 2880 35 | refresh_pattern . 0 20% 4320 36 | refresh_all_ims on 37 | -------------------------------------------------------------------------------- /gnupg/crls.d/DIR.txt: -------------------------------------------------------------------------------- 1 | v:1: 2 | -------------------------------------------------------------------------------- /gnupg/pubring.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revsys/optimized-python-docker/33c89260ad06e148594ec2e2f1716fa87fc72347/gnupg/pubring.gpg -------------------------------------------------------------------------------- /gnupg/pubring.gpg~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revsys/optimized-python-docker/33c89260ad06e148594ec2e2f1716fa87fc72347/gnupg/pubring.gpg~ -------------------------------------------------------------------------------- /gnupg/tofu.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revsys/optimized-python-docker/33c89260ad06e148594ec2e2f1716fa87fc72347/gnupg/tofu.db -------------------------------------------------------------------------------- /gnupg/trustdb.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revsys/optimized-python-docker/33c89260ad06e148594ec2e2f1716fa87fc72347/gnupg/trustdb.gpg -------------------------------------------------------------------------------- /init-functions: -------------------------------------------------------------------------------- 1 | # /lib/lsb/init-functions for Debian -*- shell-script -*- 2 | # 3 | #Copyright (c) 2002-08 Chris Lawrence 4 | #All rights reserved. 5 | # 6 | #Redistribution and use in source and binary forms, with or without 7 | #modification, are permitted provided that the following conditions 8 | #are met: 9 | #1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | #2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | #3. Neither the name of the author nor the names of other contributors 15 | # may be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | #THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | #IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | #ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 22 | #LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | #CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | #SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | #BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | #WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 27 | #OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | #EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | start_daemon () { 31 | local force nice pidfile exec args OPTIND 32 | force="" 33 | nice=0 34 | pidfile=/dev/null 35 | 36 | OPTIND=1 37 | while getopts fn:p: opt ; do 38 | case "$opt" in 39 | f) force="force";; 40 | n) nice="$OPTARG";; 41 | p) pidfile="$OPTARG";; 42 | esac 43 | done 44 | 45 | shift $(($OPTIND - 1)) 46 | if [ "$1" = '--' ]; then 47 | shift 48 | fi 49 | 50 | exec="$1"; shift 51 | 52 | args="--start --nicelevel $nice --quiet --oknodo" 53 | if [ "$force" ]; then 54 | /sbin/start-stop-daemon $args \ 55 | --chdir "$PWD" --startas $exec --pidfile /dev/null -- "$@" 56 | elif [ $pidfile ]; then 57 | /sbin/start-stop-daemon $args \ 58 | --chdir "$PWD" --exec $exec --oknodo --pidfile "$pidfile" -- "$@" 59 | else 60 | /sbin/start-stop-daemon $args --chdir "$PWD" --exec $exec -- "$@" 61 | fi 62 | } 63 | 64 | pidofproc () { 65 | local pidfile base status specified pid OPTIND 66 | pidfile= 67 | specified= 68 | 69 | OPTIND=1 70 | while getopts p: opt ; do 71 | case "$opt" in 72 | p) pidfile="$OPTARG" 73 | specified="specified" 74 | ;; 75 | esac 76 | done 77 | shift $(($OPTIND - 1)) 78 | if [ $# -ne 1 ]; then 79 | echo "$0: invalid arguments" >&2 80 | return 4 81 | fi 82 | 83 | base=${1##*/} 84 | if [ ! "$specified" ]; then 85 | pidfile="/var/run/$base.pid" 86 | fi 87 | 88 | if [ -n "${pidfile:-}" ]; then 89 | if [ -e "$pidfile" ]; then 90 | if [ -r "$pidfile" ]; then 91 | read pid < "$pidfile" 92 | if [ -n "${pid:-}" ]; then 93 | if $(kill -0 "${pid:-}" 2> /dev/null); then 94 | echo "$pid" || true 95 | return 0 96 | elif ps "${pid:-}" >/dev/null 2>&1; then 97 | echo "$pid" || true 98 | return 0 # program is running, but not owned by this user 99 | else 100 | return 1 # program is dead and /var/run pid file exists 101 | fi 102 | fi 103 | else 104 | return 4 # pid file not readable, hence status is unknown. 105 | fi 106 | else 107 | # pid file doesn't exist, try to find the pid nevertheless 108 | if [ -x /bin/pidof ] && [ ! "$specified" ]; then 109 | status="0" 110 | /bin/pidof -o %PPID -x $1 || status="$?" 111 | if [ "$status" = 1 ]; then 112 | return 3 # program is not running 113 | fi 114 | return 0 115 | fi 116 | return 3 # specified pid file doesn't exist, program probably stopped 117 | fi 118 | fi 119 | if [ "$specified" ]; then 120 | return 3 # almost certain it's not running 121 | fi 122 | return 4 # Unable to determine status 123 | } 124 | 125 | # start-stop-daemon uses the same algorithm as "pidofproc" above. 126 | killproc () { 127 | local pidfile sig status base name_param is_term_sig OPTIND 128 | pidfile= 129 | name_param= 130 | is_term_sig= 131 | 132 | OPTIND=1 133 | while getopts p: opt ; do 134 | case "$opt" in 135 | p) pidfile="$OPTARG";; 136 | esac 137 | done 138 | shift $(($OPTIND - 1)) 139 | 140 | base=${1##*/} 141 | if [ ! $pidfile ]; then 142 | name_param="--name $base --pidfile /var/run/$base.pid" 143 | else 144 | name_param="--pidfile $pidfile" 145 | fi 146 | 147 | sig=$(echo ${2:-} | sed -e 's/^-\(.*\)/\1/') 148 | sig=$(echo $sig | sed -e 's/^SIG\(.*\)/\1/') 149 | if [ "$sig" = 15 ] || [ "$sig" = TERM ]; then 150 | is_term_sig="terminate_signal" 151 | fi 152 | status=0 153 | if [ ! "$is_term_sig" ]; then 154 | if [ -n "$sig" ]; then 155 | /sbin/start-stop-daemon --stop --signal "$sig" \ 156 | --quiet $name_param || status="$?" 157 | else 158 | /sbin/start-stop-daemon --stop \ 159 | --retry 5 \ 160 | --quiet $name_param || status="$?" 161 | fi 162 | else 163 | /sbin/start-stop-daemon --stop --quiet \ 164 | --oknodo $name_param || status="$?" 165 | fi 166 | if [ "$status" = 1 ]; then 167 | if [ -z "$sig" ]; then 168 | return 0 169 | fi 170 | return 3 # program is not running 171 | fi 172 | 173 | if [ "$status" = 0 ] && [ "$is_term_sig" ] && [ "$pidfile" ]; then 174 | pidofproc -p "$pidfile" "$1" >/dev/null || rm -f "$pidfile" 175 | fi 176 | return 0 177 | } 178 | 179 | # Return LSB status 180 | status_of_proc () { 181 | local pidfile daemon name status OPTIND 182 | 183 | pidfile= 184 | OPTIND=1 185 | while getopts p: opt ; do 186 | case "$opt" in 187 | p) pidfile="$OPTARG";; 188 | esac 189 | done 190 | shift $(($OPTIND - 1)) 191 | 192 | if [ -n "$pidfile" ]; then 193 | pidfile="-p $pidfile" 194 | fi 195 | daemon="$1" 196 | name="$2" 197 | 198 | status="0" 199 | pidofproc $pidfile $daemon >/dev/null || status="$?" 200 | if [ "$status" = 0 ]; then 201 | log_success_msg "$name is running" 202 | return 0 203 | elif [ "$status" = 4 ]; then 204 | log_failure_msg "could not access PID file for $name" 205 | return $status 206 | else 207 | log_failure_msg "$name is not running" 208 | return $status 209 | fi 210 | } 211 | 212 | log_use_fancy_output () { 213 | TPUT=/usr/bin/tput 214 | EXPR=/usr/bin/expr 215 | if [ -t 1 ] && 216 | [ "x${TERM:-}" != "x" ] && 217 | [ "x${TERM:-}" != "xdumb" ] && 218 | [ -x $TPUT ] && [ -x $EXPR ] && 219 | $TPUT hpa 60 >/dev/null 2>&1 && 220 | $TPUT setaf 1 >/dev/null 2>&1 221 | then 222 | [ -z $FANCYTTY ] && FANCYTTY=1 || true 223 | else 224 | FANCYTTY=0 225 | fi 226 | case "$FANCYTTY" in 227 | 1|Y|yes|true) true;; 228 | *) false;; 229 | esac 230 | } 231 | 232 | log_success_msg () { 233 | if [ -n "${1:-}" ]; then 234 | log_begin_msg $@ 235 | fi 236 | log_end_msg 0 237 | } 238 | 239 | log_failure_msg () { 240 | if [ -n "${1:-}" ]; then 241 | log_begin_msg $@ "..." 242 | fi 243 | log_end_msg 1 || true 244 | } 245 | 246 | log_warning_msg () { 247 | if [ -n "${1:-}" ]; then 248 | log_begin_msg $@ "..." 249 | fi 250 | log_end_msg 255 || true 251 | } 252 | 253 | # 254 | # NON-LSB HELPER FUNCTIONS 255 | # 256 | # int get_lsb_header_val (char *scriptpathname, char *key) 257 | get_lsb_header_val () { 258 | if [ ! -f "$1" ] || [ -z "${2:-}" ]; then 259 | return 1 260 | fi 261 | LSB_S="### BEGIN INIT INFO" 262 | LSB_E="### END INIT INFO" 263 | sed -n "/$LSB_S/,/$LSB_E/ s/# $2: \+\(.*\)/\1/p" "$1" 264 | } 265 | 266 | # If the currently running init daemon is upstart, return zero; if the 267 | # calling init script belongs to a package which also provides a native 268 | # upstart job, it should generally exit non-zero in this case. 269 | init_is_upstart() 270 | { 271 | if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | /bin/grep -q upstart; then 272 | return 0 273 | fi 274 | return 1 275 | } 276 | 277 | # int log_begin_message (char *message) 278 | log_begin_msg () { 279 | log_begin_msg_pre "$@" 280 | if [ -z "${1:-}" ]; then 281 | return 1 282 | fi 283 | echo -n "$@" || true 284 | log_begin_msg_post "$@" 285 | } 286 | 287 | # Sample usage: 288 | # log_daemon_msg "Starting GNOME Login Manager" "gdm" 289 | # 290 | # On Debian, would output "Starting GNOME Login Manager: gdm" 291 | # On Ubuntu, would output " * Starting GNOME Login Manager..." 292 | # 293 | # If the second argument is omitted, logging suitable for use with 294 | # log_progress_msg() is used: 295 | # 296 | # log_daemon_msg "Starting remote filesystem services" 297 | # 298 | # On Debian, would output "Starting remote filesystem services:" 299 | # On Ubuntu, would output " * Starting remote filesystem services..." 300 | 301 | log_daemon_msg () { 302 | if [ -z "${1:-}" ]; then 303 | return 1 304 | fi 305 | log_daemon_msg_pre "$@" 306 | 307 | if [ -z "${2:-}" ]; then 308 | echo -n "$1:" || true 309 | return 310 | fi 311 | 312 | echo -n "$1: $2" || true 313 | log_daemon_msg_post "$@" 314 | } 315 | 316 | # #319739 317 | # 318 | # Per policy docs: 319 | # 320 | # log_daemon_msg "Starting remote file system services" 321 | # log_progress_msg "nfsd"; start-stop-daemon --start --quiet nfsd 322 | # log_progress_msg "mountd"; start-stop-daemon --start --quiet mountd 323 | # log_progress_msg "ugidd"; start-stop-daemon --start --quiet ugidd 324 | # log_end_msg 0 325 | # 326 | # You could also do something fancy with log_end_msg here based on the 327 | # return values of start-stop-daemon; this is left as an exercise for 328 | # the reader... 329 | # 330 | # On Ubuntu, one would expect log_progress_msg to be a no-op. 331 | log_progress_msg () { 332 | if [ -z "${1:-}" ]; then 333 | return 1 334 | fi 335 | echo -n " $@" || true 336 | } 337 | 338 | 339 | # int log_end_message (int exitstatus) 340 | log_end_msg () { 341 | # If no arguments were passed, return 342 | if [ -z "${1:-}" ]; then 343 | return 1 344 | fi 345 | 346 | local retval 347 | retval=$1 348 | 349 | log_end_msg_pre "$@" 350 | 351 | # Only do the fancy stuff if we have an appropriate terminal 352 | # and if /usr is already mounted 353 | if log_use_fancy_output; then 354 | RED=$( $TPUT setaf 1) 355 | YELLOW=$( $TPUT setaf 3) 356 | NORMAL=$( $TPUT op) 357 | else 358 | RED='' 359 | YELLOW='' 360 | NORMAL='' 361 | fi 362 | 363 | if [ $1 -eq 0 ]; then 364 | echo "." || true 365 | elif [ $1 -eq 255 ]; then 366 | /bin/echo -e " ${YELLOW}(warning).${NORMAL}" || true 367 | else 368 | /bin/echo -e " ${RED}failed!${NORMAL}" || true 369 | fi 370 | log_end_msg_post "$@" 371 | return $retval 372 | } 373 | 374 | log_action_msg () { 375 | log_action_msg_pre "$@" 376 | echo "$@." || true 377 | log_action_msg_post "$@" 378 | } 379 | 380 | log_action_begin_msg () { 381 | log_action_begin_msg_pre "$@" 382 | echo -n "$@..." || true 383 | log_action_begin_msg_post "$@" 384 | } 385 | 386 | log_action_cont_msg () { 387 | echo -n "$@..." || true 388 | } 389 | 390 | log_action_end_msg () { 391 | local end 392 | log_action_end_msg_pre "$@" 393 | if [ -z "${2:-}" ]; then 394 | end="." 395 | else 396 | end=" ($2)." 397 | fi 398 | 399 | if [ $1 -eq 0 ]; then 400 | echo "done${end}" || true 401 | else 402 | if log_use_fancy_output; then 403 | RED=$( $TPUT setaf 1) 404 | NORMAL=$( $TPUT op) 405 | /bin/echo -e "${RED}failed${end}${NORMAL}" || true 406 | else 407 | echo "failed${end}" || true 408 | fi 409 | fi 410 | log_action_end_msg_post "$@" 411 | } 412 | 413 | # Pre&Post empty function declaration, to be overriden from /lib/lsb/init-functions.d/* 414 | log_daemon_msg_pre () { :; } 415 | log_daemon_msg_post () { :; } 416 | log_begin_msg_pre () { :; } 417 | log_begin_msg_post () { :; } 418 | log_end_msg_pre () { :; } 419 | log_end_msg_post () { :; } 420 | log_action_msg_pre () { :; } 421 | log_action_msg_post () { :; } 422 | log_action_begin_msg_pre () { :; } 423 | log_action_begin_msg_post () { :; } 424 | log_action_end_msg_pre () { :; } 425 | log_action_end_msg_post () { :; } 426 | 427 | # Include hooks from other packages in /lib/lsb/init-functions.d 428 | for hook in $(run-parts --lsbsysinit --list /lib/lsb/init-functions.d 2>/dev/null); do 429 | [ -r $hook ] && . $hook || true 430 | done 431 | 432 | FANCYTTY= 433 | [ -e /etc/lsb-base-logging.sh ] && . /etc/lsb-base-logging.sh || true 434 | -------------------------------------------------------------------------------- /ipython_config.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=undefined-variable,missing-docstring 2 | 3 | import os 4 | HOME = os.environ['HOME'] 5 | 6 | 7 | VI_MODE = True if bool(os.environ.get('VIMODE', '')) else False 8 | 9 | if VI_MODE: 10 | c.TerminalInteractiveShell.editing_mode = 'vi' 11 | 12 | c.TerminalInteractiveShell.display_completions = 'readlinelike' 13 | c.TerminalInteractiveShell.display_page = True 14 | c.TerminalInteractiveShell.automagic = False 15 | c.IPCompleter.merge_completions = False 16 | c.IPCompleter.omit__names = 0 17 | -------------------------------------------------------------------------------- /revsys_Python-Optimized-Images_and_Benchmark-Data.md: -------------------------------------------------------------------------------- 1 | ### Hardware Platform 2 | 3 | ``` 4 | Architecture: x86_64 5 | CPU op-mode(s): 32-bit, 64-bit 6 | Byte Order: Little Endian 7 | CPU(s): 36 8 | On-line CPU(s) list: 0-35 9 | Thread(s) per core: 2 10 | Core(s) per socket: 9 11 | Socket(s): 2 12 | NUMA node(s): 2 13 | Vendor ID: GenuineIntel 14 | CPU family: 6 15 | Model: 63 16 | Model name: Intel(R) Xeon(R) CPU E5-2666 v3 @ 2.90GHz 17 | Stepping: 2 18 | CPU MHz: 2900.000 19 | CPU max MHz: 3500.0000 20 | CPU min MHz: 1200.0000 21 | BogoMIPS: 5876.39 22 | Hypervisor vendor: Xen 23 | Virtualization type: full 24 | L1d cache: 32K 25 | L1i cache: 32K 26 | L2 cache: 256K 27 | L3 cache: 25600K 28 | NUMA node0 CPU(s): 0-8,18-26 29 | NUMA node1 CPU(s): 9-17,27-35 30 | ``` 31 | 32 | aka: **AWS EC2 c4.8xlarge** 33 | 34 | 35 | ### Docker CLI args 36 | 37 | each container was initialized as follows: 38 | 39 | ``` 40 | docker run -d --privileged --cpuset-cpus NN revolutionsystems/python:3.x.y-wee 41 | ``` 42 | 43 | The `c4.8xlarge` EC2 instance type has 18 dual-threaded cores. Each Python 44 | version’s test runs were executed on seperate CPU dies located on disparate NUMA 45 | nodes. 46 | 47 | The host was **not** reconfigured for kernel-level CPU isolation. 48 | 49 | ### Container Setup 50 | 51 | Installed packages: 52 | 53 | * gcc 54 | * g++ 55 | * gfortran 56 | * make 57 | * autoconf 58 | * automake 59 | * libtool 60 | 61 | ### (py)Performance CLI 62 | 63 | ``` 64 | 65 | python -m performance run --affinity NN -b all --append py3xy_(opt|nonopt).json 66 | 67 | ``` 68 | 69 | --- 70 | 71 | ### Python 3.6.3 (pgo + lto) 72 | 73 | | Benchmark | unoptimized | optimized | 74 | |------------------------|---------------------------|-------------------------------| 75 | | 2to3 | 642 ms | 588 ms: 1.09x faster (-9%) | 76 | | chameleon | 21.2 ms | 19.6 ms: 1.08x faster (-7%) | 77 | | chaos | 252 ms | 228 ms: 1.11x faster (-10%) | 78 | | crypto_pyaes | 209 ms | 200 ms: 1.05x faster (-4%) | 79 | | deltablue | 17.3 ms | 14.8 ms: 1.17x faster (-14%) | 80 | | django_template | 321 ms | 282 ms: 1.14x faster (-12%) | 81 | | dulwich_log | 158 ms | 140 ms: 1.13x faster (-11%) | 82 | | fannkuch | 962 ms | 918 ms: 1.05x faster (-5%) | 83 | | float | 221 ms | 214 ms: 1.03x faster (-3%) | 84 | | genshi_text | 67.1 ms | 61.5 ms: 1.09x faster (-8%) | 85 | | genshi_xml | 140 ms | 127 ms: 1.10x faster (-9%) | 86 | | go | 565 ms | 488 ms: 1.16x faster (-14%) | 87 | | hexiom | 22.7 ms | 20.8 ms: 1.09x faster (-9%) | 88 | | html5lib | 197 ms | 173 ms: 1.14x faster (-12%) | 89 | | json_dumps | 23.3 ms | 21.6 ms: 1.08x faster (-7%) | 90 | | json_loads | 50.0 us | 43.6 us: 1.15x faster (-13%) | 91 | | logging_format | 25.4 us | 22.0 us: 1.15x faster (-13%) | 92 | | logging_silent | 737 ns | 642 ns: 1.15x faster (-13%) | 93 | | logging_simple | 21.3 us | 18.3 us: 1.16x faster (-14%) | 94 | | mako | 41.9 ms | 37.7 ms: 1.11x faster (-10%) | 95 | | meteor_contest | 184 ms | 183 ms: 1.01x faster (-1%) | 96 | | nbody | 232 ms | 232 ms: 1.00x faster (-0%) | 97 | | nqueens | 205 ms | 192 ms: 1.07x faster (-6%) | 98 | | pathlib | 36.7 ms | 32.1 ms: 1.14x faster (-12%) | 99 | | pickle | 19.6 us | 17.1 us: 1.15x faster (-13%) | 100 | | pickle_dict | 59.9 us | 50.1 us: 1.20x faster (-16%) | 101 | | pickle_list | 8.12 us | 6.55 us: 1.24x faster (-19%) | 102 | | pickle_pure_python | 1.10 ms | 946 us: 1.16x faster (-14%) | 103 | | pidigits | 277 ms | 273 ms: 1.01x faster (-1%) | 104 | | python_startup | 15.8 ms | 14.7 ms: 1.07x faster (-7%) | 105 | | python_startup_no_site | 9.51 ms | 8.94 ms: 1.06x faster (-6%) | 106 | | raytrace | 1.24 sec | 1.09 sec: 1.14x faster (-12%) | 107 | | regex_compile | 389 ms | 355 ms: 1.10x faster (-9%) | 108 | | regex_dna | 269 ms | 247 ms: 1.09x faster (-8%) | 109 | | regex_effbot | 4.64 ms | 4.57 ms: 1.01x faster (-1%) | 110 | | regex_v8 | 41.9 ms | 39.7 ms: 1.06x faster (-5%) | 111 | | richards | 176 ms | 147 ms: 1.20x faster (-17%) | 112 | | scimark_fft | 616 ms | 602 ms: 1.02x faster (-2%) | 113 | | scimark_lu | 477 ms | 452 ms: 1.06x faster (-5%) | 114 | | scimark_monte_carlo | 221 ms | 204 ms: 1.08x faster (-8%) | 115 | | scimark_sor | 464 ms | 411 ms: 1.13x faster (-11%) | 116 | | spectral_norm | 253 ms | 240 ms: 1.05x faster (-5%) | 117 | | sqlalchemy_imperative | 65.2 ms | 61.5 ms: 1.06x faster (-6%) | 118 | | sqlite_synth | 6.09 us | 5.86 us: 1.04x faster (-4%) | 119 | | sympy_expand | 903 ms | 836 ms: 1.08x faster (-7%) | 120 | | sympy_integrate | 42.1 ms | 39.0 ms: 1.08x faster (-7%) | 121 | | sympy_sum | 186 ms | 172 ms: 1.08x faster (-7%) | 122 | | sympy_str | 401 ms | 368 ms: 1.09x faster (-8%) | 123 | | telco | 14.0 ms | 12.0 ms: 1.17x faster (-14%) | 124 | | tornado_http | 411 ms | 377 ms: 1.09x faster (-8%) | 125 | | unpickle | 31.6 us | 28.7 us: 1.10x faster (-9%) | 126 | | unpickle_list | 7.07 us | 5.90 us: 1.20x faster (-16%) | 127 | | unpickle_pure_python | 803 us | 711 us: 1.13x faster (-12%) | 128 | | xml_etree_parse | 232 ms | 224 ms: 1.04x faster (-3%) | 129 | | xml_etree_iterparse | 195 ms | 193 ms: 1.01x faster (-1%) | 130 | | xml_etree_generate | 213 ms | 204 ms: 1.04x faster (-4%) | 131 | | xml_etree_process | 175 ms | 164 ms: 1.07x faster (-6%) | 132 | 133 | Not significant: 134 | 135 | * scimark_sparse_mat_mult 136 | * sqlalchemy_declarative 137 | * unpack_sequence 138 | 139 | --- 140 | 141 | ### Python 3.5.4 (pgo + lto) 142 | 143 | | Benchmark | unoptimized | optimized | 144 | --------------------------|--------------|-------------------------------| 145 | | 2to3 | 652 ms | 588 ms: 1.11x faster (-10%) | 146 | | chameleon | 20.6 ms | 17.9 ms: 1.15x faster (-13%) | 147 | | chaos | 260 ms | 233 ms: 1.12x faster (-10%) | 148 | | crypto_pyaes | 225 ms | 208 ms: 1.08x faster (-8%) | 149 | | deltablue | 16.4 ms | 14.2 ms: 1.16x faster (-14%) | 150 | | django_template | 316 ms | 269 ms: 1.17x faster (-15%) | 151 | | dulwich_log | 156 ms | 138 ms: 1.13x faster (-12%) | 152 | | fannkuch | 978 ms | 946 ms: 1.03x faster (-3%) | 153 | | float | 233 ms | 219 ms: 1.06x faster (-6%) | 154 | | genshi_text | 66.6 ms | 59.2 ms: 1.13x faster (-11%) | 155 | | genshi_xml | 139 ms | 122 ms: 1.14x faster (-12%) | 156 | | go | 535 ms | 461 ms: 1.16x faster (-14%) | 157 | | hexiom | 22.7 ms | 20.6 ms: 1.10x faster (-9%) | 158 | | html5lib | 203 ms | 173 ms: 1.17x faster (-14%) | 159 | | json_dumps | 23.3 ms | 21.2 ms: 1.10x faster (-9%) | 160 | | json_loads | 48.9 us | 42.2 us: 1.16x faster (-14%) | 161 | | logging_format | 24.3 us | 20.8 us: 1.17x faster (-14%) | 162 | | logging_silent | 768 ns | 675 ns: 1.14x faster (-12%) | 163 | | logging_simple | 20.1 us | 17.2 us: 1.17x faster (-14%) | 164 | | mako | 39.0 ms | 35.4 ms: 1.10x faster (-9%) | 165 | | mdp | 5.76 sec | 5.10 sec: 1.13x faster (-11%) | 166 | | meteor_contest | 194 ms | 178 ms: 1.09x faster (-8%) | 167 | | nbody | 234 ms | 222 ms: 1.06x faster (-5%) | 168 | | nqueens | 212 ms | 194 ms: 1.09x faster (-8%) | 169 | | pathlib | 34.7 ms | 30.1 ms: 1.15x faster (-13%) | 170 | | pickle | 20.5 us | 17.5 us: 1.17x faster (-14%) | 171 | | pickle_dict | 62.2 us | 52.1 us: 1.19x faster (-16%) | 172 | | pickle_list | 8.73 us | 6.81 us: 1.28x faster (-22%) | 173 | | pickle_pure_python | 1.03 ms | 915 us: 1.13x faster (-11%) | 174 | | pidigits | 277 ms | 274 ms: 1.01x faster (-1%) | 175 | | python_startup | 18.3 ms | 17.1 ms: 1.07x faster (-7%) | 176 | | python_startup_no_site | 10.0 ms | 9.39 ms: 1.07x faster (-7%) | 177 | | raytrace | 1.29 sec | 1.13 sec: 1.14x faster (-12%) | 178 | | regex_compile | 319 ms | 287 ms: 1.11x faster (-10%) | 179 | | regex_dna | 268 ms | 244 ms: 1.10x faster (-9%) | 180 | | regex_effbot | 5.29 ms | 5.10 ms: 1.04x faster (-3%) | 181 | | regex_v8 | 42.7 ms | 39.9 ms: 1.07x faster (-7%) | 182 | | richards | 169 ms | 144 ms: 1.17x faster (-15%) | 183 | | scimark_fft | 617 ms | 591 ms: 1.04x faster (-4%) | 184 | | scimark_lu | 500 ms | 458 ms: 1.09x faster (-8%) | 185 | | scimark_monte_carlo | 216 ms | 202 ms: 1.07x faster (-6%) | 186 | | scimark_sor | 487 ms | 430 ms: 1.13x faster (-12%) | 187 | | scimark_sparse_mat_mult | 7.47 ms | 7.30 ms: 1.02x faster (-2%) | 188 | | spectral_norm | 289 ms | 285 ms: 1.01x faster (-1%) | 189 | | sqlalchemy_declarative | 308 ms | 286 ms: 1.07x faster (-7%) | 190 | | sqlalchemy_imperative | 65.4 ms | 60.4 ms: 1.08x faster (-8%) | 191 | | sqlite_synth | 6.15 us | 5.95 us: 1.03x faster (-3%) | 192 | | sympy_expand | 995 ms | 904 ms: 1.10x faster (-9%) | 193 | | sympy_integrate | 42.9 ms | 39.0 ms: 1.10x faster (-9%) | 194 | | sympy_sum | 211 ms | 191 ms: 1.11x faster (-10%) | 195 | | sympy_str | 429 ms | 388 ms: 1.11x faster (-10%) | 196 | | telco | 14.1 ms | 13.0 ms: 1.08x faster (-8%) | 197 | | tornado_http | 415 ms | 378 ms: 1.10x faster (-9%) | 198 | | unpack_sequence | 106 ns | 87.0 ns: 1.22x faster (-18%) | 199 | | unpickle | 31.4 us | 26.7 us: 1.18x faster (-15%) | 200 | | unpickle_list | 9.46 us | 7.85 us: 1.20x faster (-17%) | 201 | | unpickle_pure_python | 783 us | 701 us: 1.12x faster (-10%) | 202 | | xml_etree_parse | 272 ms | 256 ms: 1.06x faster (-6%) | 203 | | xml_etree_iterparse | 424 ms | 362 ms: 1.17x faster (-15%) | 204 | | xml_etree_generate | 252 ms | 231 ms: 1.09x faster (-8%) | 205 | | xml_etree_process | 197 ms | 180 ms: 1.09x faster (-9%) | 206 | 207 | 208 | ### Related Discussion Threads 209 | 210 | * [Link Time Optimizations support for GCC and CLANG](https://bugs.python.org/issue25702) 211 | * [Discussion regarding default LTO/PGO builds for Docker Library Python Images](https://github.com/docker-library/python/issues/160) 212 | * [Alpine Linux (Python) build issue](https://github.com/alpinelinux/aports/pull/1775) 213 | --------------------------------------------------------------------------------