-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Use only aomenc and dav1d AVIF codecs to decrease wheel size #8858
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
base: main
Are you sure you want to change the base?
Conversation
I'm going to see what effect @wantehchang's suggestion has on the wheel sizes. |
50c6633
to
86464ca
Compare
Also build libavif as a shared library. When it is built as a static library, the dependency library files are combined into a single archive. But when they are linked as a shared library, the linker is able to remove unused objects. This yields a modest but not insignificant file size reduction.
86464ca
to
8b4e66e
Compare
-DCMAKE_BUILD_TYPE=Release \ | ||
-DBUILD_SHARED_LIBS=OFF \ | ||
-DBUILD_SHARED_LIBS=ON \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to change BUILD_SHARED_LIBS
to ON
? It is still OFF
in winbuild/build_prepare.py.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did. Using the linker to generate the shared library file allows it to remove unused objects. There is a mechanism for including shared library dependencies of a python extension inside a wheel file on macOS and linux (delocate and auditwheel, respectively) but there is no such standard mechanism for doing the same on windows. So the windows library has to be built statically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The savings are modest, about 0.5 MB, but there isn't any cost to switching to a shared library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you say 0.5mb, I presume that's per wheel. Which wheel(s) were you using to check that?
ENABLE_NASM=ON is unnecessary...it forces the use of nasm over yasm if both are available, but only nasm is installed in the wheel build environments. And CMAKE_MODULE_PATH was only necessary when we used pre-built rav1e binaries.
Frankie: Here are some other ideas to further reduce the binary size.
|
8328cd4
to
7696f13
Compare
Tests/test_file_avif.py
Outdated
@@ -146,7 +146,7 @@ def test_write_rgb(self, tmp_path: Path) -> None: | |||
|
|||
# avifdec hopper.avif avif/hopper_avif_write.png | |||
assert_image_similar_tofile( | |||
reloaded, "Tests/images/avif/hopper_avif_write.png", 6.02 | |||
reloaded, "Tests/images/avif/hopper_avif_write.png", 6.14 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to relax the average pixel difference threshold?
Tests/test_file_avif.py
Outdated
@@ -146,7 +146,7 @@ def test_write_rgb(self, tmp_path: Path) -> None: | |||
|
|||
# avifdec hopper.avif avif/hopper_avif_write.png | |||
assert_image_similar_tofile( | |||
reloaded, "Tests/images/avif/hopper_avif_write.png", 6.02 | |||
reloaded, "Tests/images/avif/hopper_avif_write.png", 6.14 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to relax the average pixel difference threshold?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this temporarily; it has since been changed back. It seems that when aom is built with -Os
for manylinux2014 wheels (i.e. CentOS 7 with gcc 10) the encoder behavior changes. I think this must be a bug with gcc? I can't see why compiler optimization flags should affect the actual behavior of an application. I've opted to use -DCMAKE_BUILD_TYPE=Release
for these el7 builds.
7696f13
to
f5d13cd
Compare
f5d13cd
to
deb1c9d
Compare
The alternative #8869 removed installation of things like |
They have been removed here as well. The only difference between this PR and that one is that this one uses the dav1d decoder instead of AOM's. And it leaves open the possibility of users compiling for more codecs |
@hugovk Another difference between this PR and the alternative #8869 is that this PR has additional changes to the build system to reduce the binary size, specifically:
For a fair comparison, these changes should be applied to the alternative #8869. |
@radarhere After I merged your change, I noticed that the windows wheels had increased to 7.1 MiB from 6.9 MiB, so I re-added the flag in build_prepare.py. |
It actually looks like macOS is the only platform where |
To answer this, if I download wheels from the current state of this PR, I get 522.92mb in total.
I get 488.77mb. |
Not in wheels: this is regrettable. |
@doublex Please see my comments here and clarify if you find the decision "regrettable" even with concerns expressed about wheel size? I'm not sure any feature is worth universally increasing the wheel size by a factor of 10. If we can do it optionally, then I would agree not getting it into this release is regrettable, but I'm not sure how that would work just yet. |
If anyone watching this would like to download prebuilt Pillow wheels with libavif, be aware that you can use the artifacts from the GitHub Actions of this PR, for example by going to https://github.com/python-pillow/Pillow/actions/runs/14424832221 and scrolling down. |
@aclark4life Those comments were made with regard to the initial 11.2.0 release. Those did indeed result in wheels that were as much as 4x larger and uncompressed sizes of 6x or more. Because of various size optimizations, the AVIF feature now only increases both by about 60% (a little more or less, depending on the platform). I think the door was left open for AVIF ending up in the 11.3.0 wheels, judging from @hugovk's comment here: #8869 (comment). The number of people who would be negatively affected by the addition to their virtualenv of a 50 MB file is, I suspect, reasonably large. But 5 MB? For people looking to use Pillow in such resource-constrained environments, we could perhaps provide guidance on how to delete undesired bundled dependencies. |
@@ -105,7 +105,7 @@ function build_libavif { | |||
python3 -m pip install meson ninja | |||
|
|||
if [[ "$PLAT" == "x86_64" ]] || [ -n "$SANITIZER" ]; then | |||
build_simple nasm 2.16.03 https://www.nasm.us/pub/nasm/releasebuilds/2.16.03 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fyi, if the nasm.us outage is temporary, then the presence of https://github.com/python-pillow/pillow-depends/blob/main/nasm-2.16.03.tar.gz should allow this to continue working until it comes back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure it's temporary, since it's been down now for nearly a week. But I had forgotten about the pillow-depends backup. I've reverted this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into that. The source tarballs on nasm.us have the autotools artifacts included. So we couldn't just swap out with a GitHub tag archive here, as we would need to update this script to call autogen.sh. Not an insurmountable obstacle, but it couldn't use the build_simple
bash function from multibuild.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nasm.us is back now.
if [[ "$MB_ML_VER" == 2014 ]] && [[ "$PLAT" == "x86_64" ]]; then | ||
build_type=Release | ||
fi | ||
libavif_cmake_flags+=(-DCMAKE_SHARED_LINKER_FLAGS_INIT="-Wl,--strip-all,-z,relro,-z,now") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I compare the wheel sizes from https://github.com/python-pillow/Pillow/actions/runs/14773502497?pr=8858,
with a commit where I remove this line, I find that https://github.com/radarhere/Pillow/actions/runs/14773542213 is actually 8kb smaller.
If this line is purely about reducing file size, then I don't think it's fulfilling that role.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it's because there is a typo. I'll fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually had to do with a cmake policy that was disabling LTO on linux. See the build here: https://github.com/fdintino/Pillow/actions/runs/14777836226
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the PR description, I've updated the paragraph that begins with "In absolute terms,..." and the last column of the table with the latest wheel and shared library file sizes. You can look at the github diff under the list of edits to see the change.
@@ -51,6 +51,7 @@ LIBWEBP_VERSION=1.5.0 | |||
BZIP2_VERSION=1.0.8 | |||
LIBXCB_VERSION=1.17.0 | |||
BROTLI_VERSION=1.1.0 | |||
LIBAVIF_VERSION=1.2.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LIBAVIF_VERSION=1.2.1 | |
LIBAVIF_VERSION=1.3.0 |
libavif 1.3.0 has been released - https://github.com/AOMediaCodec/libavif/releases/tag/v1.3.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggested change. I suggest two related changes if you will only be building with libavif v1.3.0 or later.
- Please delete all occurrences of
CONFIG_AV1_DECODER=0
in your build files (including the comments). libavif v1.3.0 or later setsCONFIG_AV1_DECODER
to 0 if you pass-DAVIF_CODEC_AOM_DECODE=OFF
to libavif. - Please try removing
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
. We fixed all the oldcmake_minimum_required()
versions in libavif v1.3.0. You should not need this workaround now.
@@ -51,6 +51,7 @@ LIBWEBP_VERSION=1.5.0 | |||
BZIP2_VERSION=1.0.8 | |||
LIBXCB_VERSION=1.17.0 | |||
BROTLI_VERSION=1.1.0 | |||
LIBAVIF_VERSION=1.2.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggested change. I suggest two related changes if you will only be building with libavif v1.3.0 or later.
- Please delete all occurrences of
CONFIG_AV1_DECODER=0
in your build files (including the comments). libavif v1.3.0 or later setsCONFIG_AV1_DECODER
to 0 if you pass-DAVIF_CODEC_AOM_DECODE=OFF
to libavif. - Please try removing
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
. We fixed all the oldcmake_minimum_required()
versions in libavif v1.3.0. You should not need this workaround now.
# CONFIG_AV1_DECODER=0 is a flag for libaom (included as a subproject of | ||
# libavif) to disable the compilation and inclusion of aom's AV1 decoder. | ||
# CONFIG_AV1_HIGHBITDEPTH=0 is another flag for libaom that disables support | ||
# for encoding high bit depth images. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not disbale support for encoding high bit depth images. Most AVIF HDR images are 10 bits. Can we avoid this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pillow does not yet support high bit depth images (see #1888). It is currently a work-in-progress, but at the moment there can only ever be 8-bit images passed to the C API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the reply.
Nit: Change "another flag" to "a flag" because we are removing the previous paragraph (lines 131-132).
We can look into disabling support for decoding high bit depth images in the dav1d library. It has this build option:
option('bitdepths',
type: 'array',
choices: ['8', '16'],
description: 'Enable only specified bitdepths')
But it seems hard to pass this build option to dav1d through libavif's cmake command line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we might still want to allow decoding of 10-bit images, even if that necessarily means downsampling to 8 bit to load them into Pillow.
@@ -116,7 +119,7 @@ def cmd_msbuild( | |||
"HARFBUZZ": "11.1.0", | |||
"JPEGTURBO": "3.1.0", | |||
"LCMS2": "2.17", | |||
"LIBAVIF": "1.2.1", | |||
"LIBAVIF": "1.3.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also change the libavif version to 1.3.0 in Pillow/depends/install_libavif.sh.
Fixes #8856
This removes all libavif codecs except for AOM for encoding and dav1d for decoding. It also builds libavif as a shared library on linux and macOS, which modestly decreases the file size because it allows the linker to omit unused objects from the codec libraries. Building with
-Os
and-flto
reduces the size by another further 0.7-1.5MB.Below is a comparison of the average CPython wheel sizes by platform for the wheels (MiB) prior to the merge of the avif pull request, the current 11.2.0 wheels, and the wheels from this pull request. An additional few-hundred K could be saved by using aom's decoder instead of dav1d, but I think the benefits of dav1d outweigh those cost savings.
Since Pillow does not yet support multi-channel high bit depth images, I've compiled libaom without high-bit-depth support. This further reduces wheel sizes by ~200KiB.
In absolute terms, the uncompressed libavif shared library file size is somewhere between 2.8MiB (macOS ARM) and 5.0MiB (musllinux x86_64), with the exception of manylinux2014 x86_64 which, because of a gcc bug, is missing some optimizations and so is 7.4MiB.
-flto -Os