Skip to content
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

Allow users to pass max_threads to the avif encoder via Image.save #49

Closed
wants to merge 0 commits into from

Conversation

yit-b
Copy link
Contributor

@yit-b yit-b commented May 9, 2024

It's not always desirable to set max_threads equal to the number of cpus on the host machine - especially in shared or containerized environments. Performance is often significantly worse if you have too many CPUs (see benchmarks below).

This PR gives users control over how many threads will be used for encoding by adding a max_threads argument accepted by Image.save(). Example:

save_test.py

import time
import pillow_avif
from PIL import Image

if __name__ == "__main__":
    img = Image.open("tests/images/flower.jpg")

    n_warmups = 1
    n_iters = 10

    for n_threads in range(0, 17):
        for _ in range(n_warmups):
            img.save("flower.avif", quality=80, max_threads=n_threads)
        start = time.time()
        for _ in range(n_iters):
            img.save("flower.avif", quality=80, max_threads=n_threads)
        print(
            f"N threads: {n_threads}. Avg time: {((time.time() - start) / n_iters * 1000):.2f} ms/img"
        )

Output:

N threads: 0. Avg time: 71.45 ms/img
N threads: 1. Avg time: 128.72 ms/img
N threads: 2. Avg time: 76.32 ms/img
N threads: 3. Avg time: 61.18 ms/img
N threads: 4. Avg time: 62.81 ms/img
N threads: 5. Avg time: 61.81 ms/img
N threads: 6. Avg time: 61.33 ms/img
N threads: 7. Avg time: 65.15 ms/img
N threads: 8. Avg time: 62.29 ms/img
N threads: 9. Avg time: 63.38 ms/img
N threads: 10. Avg time: 62.56 ms/img
N threads: 11. Avg time: 61.89 ms/img
N threads: 12. Avg time: 64.02 ms/img
N threads: 13. Avg time: 64.10 ms/img
N threads: 14. Avg time: 66.18 ms/img
N threads: 15. Avg time: 64.52 ms/img
N threads: 16. Avg time: 64.76 ms/img

The default behavior remains unchanged. It is 0 if not specified which is set to the cpu count.

Maybe this should be changed to a more reasonable default since performance seems the same with max_threads=0 (all CPUs) as it does with just 2 - probably because of contention? Maybe changing the default is outside the scope of this PR but I'd like to be able to tailor the parallelism to my compute environment.

Reproduce my tests:

conda activate foobar # This is some new empty conda environment
conda install -c conda-forge pillow 'libavif>=1.0.2' aom 'python=3.10.*' pillow
cd pillow-avif-plugin
pip install --no-deps .
python save_test.py

@yit-b
Copy link
Contributor Author

yit-b commented May 9, 2024

CI failures seem unrelated to my changes.

@fdintino
Copy link
Owner

fdintino commented May 9, 2024

I think the rav1e and SVT-AV1 codecs probably make better use of max_threads. Could you run your script and post the results when you pass codec="rav1e" and codec="svt" to save()?

@yit-b
Copy link
Contributor Author

yit-b commented May 9, 2024

@fdintino

Codec: aom. N threads: 0. Avg time: 67.91 ms/img
Codec: aom. N threads: 1. Avg time: 130.41 ms/img
Codec: aom. N threads: 2. Avg time: 77.05 ms/img
Codec: aom. N threads: 3. Avg time: 61.73 ms/img
Codec: aom. N threads: 4. Avg time: 63.33 ms/img
Codec: aom. N threads: 5. Avg time: 61.71 ms/img
Codec: aom. N threads: 6. Avg time: 63.15 ms/img
Codec: aom. N threads: 7. Avg time: 64.46 ms/img
Codec: aom. N threads: 8. Avg time: 63.20 ms/img
Codec: aom. N threads: 9. Avg time: 61.67 ms/img
Codec: aom. N threads: 10. Avg time: 63.49 ms/img
Codec: aom. N threads: 11. Avg time: 62.05 ms/img
Codec: aom. N threads: 12. Avg time: 63.82 ms/img
Codec: aom. N threads: 13. Avg time: 66.58 ms/img
Codec: aom. N threads: 14. Avg time: 63.10 ms/img
Codec: aom. N threads: 15. Avg time: 65.07 ms/img
Codec: aom. N threads: 16. Avg time: 71.24 ms/img
Codec: rav1e. N threads: 0. Avg time: 742.48 ms/img
Codec: rav1e. N threads: 1. Avg time: 734.62 ms/img
Codec: rav1e. N threads: 2. Avg time: 730.26 ms/img
Codec: rav1e. N threads: 3. Avg time: 731.19 ms/img
Codec: rav1e. N threads: 4. Avg time: 736.11 ms/img
Codec: rav1e. N threads: 5. Avg time: 742.12 ms/img
Codec: rav1e. N threads: 6. Avg time: 736.13 ms/img
Codec: rav1e. N threads: 7. Avg time: 729.60 ms/img
Codec: rav1e. N threads: 8. Avg time: 729.46 ms/img
Codec: rav1e. N threads: 9. Avg time: 726.91 ms/img
Codec: rav1e. N threads: 10. Avg time: 722.95 ms/img
Codec: rav1e. N threads: 11. Avg time: 740.03 ms/img
Codec: rav1e. N threads: 12. Avg time: 724.34 ms/img
Codec: rav1e. N threads: 13. Avg time: 726.96 ms/img
Codec: rav1e. N threads: 14. Avg time: 730.37 ms/img
Codec: rav1e. N threads: 15. Avg time: 731.43 ms/img
Codec: rav1e. N threads: 16. Avg time: 728.80 ms/img
Codec: svt. N threads: 0. Avg time: 578.26 ms/img
Codec: svt. N threads: 1. Avg time: 394.54 ms/img
Codec: svt. N threads: 2. Avg time: 193.15 ms/img
Codec: svt. N threads: 3. Avg time: 208.09 ms/img
Codec: svt. N threads: 4. Avg time: 205.94 ms/img
Codec: svt. N threads: 5. Avg time: 198.58 ms/img
Codec: svt. N threads: 6. Avg time: 283.46 ms/img
Codec: svt. N threads: 7. Avg time: 278.86 ms/img
Codec: svt. N threads: 8. Avg time: 279.64 ms/img
Codec: svt. N threads: 9. Avg time: 278.56 ms/img
Codec: svt. N threads: 10. Avg time: 287.89 ms/img
Codec: svt. N threads: 11. Avg time: 285.51 ms/img
Codec: svt. N threads: 12. Avg time: 284.84 ms/img
Codec: svt. N threads: 13. Avg time: 287.96 ms/img
Codec: svt. N threads: 14. Avg time: 294.32 ms/img
Codec: svt. N threads: 15. Avg time: 282.62 ms/img
Codec: svt. N threads: 16. Avg time: 294.30 ms/img

@una-dinosauria
Copy link

@fdintino ping.

This PR makes the plugin much more usable for large-scale image compression and does not alter the default behaviour. Please let us know if there's anything else you'd like to see.

@yit-b
Copy link
Contributor Author

yit-b commented Jun 28, 2024

@fdintino gentle bump

@una-dinosauria
Copy link

Looks like the issue was closed by accident and moved to #54

@fdintino
Copy link
Owner

fdintino commented Jul 3, 2024

Yes, I cross-referenced it in the PR description there, but should have commented here as well. Thanks.

github-merge-queue bot pushed a commit to BSStudio/bss-web-file-api that referenced this pull request Jul 9, 2024
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [pillow-avif-plugin](https://togithub.com/fdintino/pillow-avif-plugin)
| `1.4.3` -> `1.4.4` |
[![age](https://developer.mend.io/api/mc/badges/age/pypi/pillow-avif-plugin/1.4.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/pillow-avif-plugin/1.4.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/pillow-avif-plugin/1.4.3/1.4.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/pillow-avif-plugin/1.4.3/1.4.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>fdintino/pillow-avif-plugin (pillow-avif-plugin)</summary>

###
[`v1.4.4`](https://togithub.com/fdintino/pillow-avif-plugin/releases/tag/v1.4.4)

[Compare
Source](https://togithub.com/fdintino/pillow-avif-plugin/compare/v1.4.3...v1.4.4)

#### What's Changed

- chore(ci): bump libavif to
[`e10e6d9`](https://togithub.com/fdintino/pillow-avif-plugin/commit/e10e6d9)-2024-07-01;
fix CI build issues by [@&#8203;fdintino](https://togithub.com/fdintino)
in
[fdintino/pillow-avif-plugin#53.
See table below for new versions (all versions are upgrades from the
1.4.3 release).

    |             |            |
    |-------------|------------|
| **libavif** | <b>1.0.3
([e10e6d9](https://togithub.com/AOMediaCodec/libavif/commit/e10e6d98e6d1dbcdd409859a924d1b607a1e06dc))</b>
|
    | **libaom**  | **3.9.1**  |
    | **dav1d**   | **1.4.3**  |
    | **SVT-AV1** | **2.1.1**  |
    | **rav1e**   | **0.7.1**  |

- feat: Allow users to pass `max_threads` to the avif encoder via
`Image.save` by [@&#8203;yit-b](https://togithub.com/yit-b) in
[fdintino/pillow-avif-plugin#54,
originally in
[fdintino/pillow-avif-plugin#49

- feat: Let users pass `max_threads` as an argument to
`_avif.AvifDecoder` by [@&#8203;yit-b](https://togithub.com/yit-b) in
[fdintino/pillow-avif-plugin#50

- chore(ci): build SVT-AV1 for aarch64 or arm64 by
[@&#8203;RaphaelVRossi](https://togithub.com/RaphaelVRossi) in
[fdintino/pillow-avif-plugin#38

- fix: keep alpha channel for images with mode P and custom transparency
by [@&#8203;fdintino](https://togithub.com/fdintino) in
[fdintino/pillow-avif-plugin#56;
fixes
[fdintino/pillow-avif-plugin#48

- fix: disable decoder strictness for `clap` and `pixi` properties by
[@&#8203;fdintino](https://togithub.com/fdintino) in
[fdintino/pillow-avif-plugin#57.
fixes
[fdintino/pillow-avif-plugin#13,
fixes
[fdintino/pillow-avif-plugin#28

- fix(ci): lint secrets permission error and macOS GHA runner homebrew
`PATH` bug by [@&#8203;fdintino](https://togithub.com/fdintino) in
[fdintino/pillow-avif-plugin#55

#### New Contributors

- [@&#8203;RaphaelVRossi](https://togithub.com/RaphaelVRossi) made their
first contribution in
[fdintino/pillow-avif-plugin#38

**Full Changelog**:
fdintino/pillow-avif-plugin@v1.4.3...v1.4.4

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/BSStudio/bss-web-file-api).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40MjUuMSIsInVwZGF0ZWRJblZlciI6IjM3LjQyNS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants