-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Optimized ImageStat.Stat.count #7599
Conversation
The new implementation uses "sum" instead of the construct "functools.reduce(operator.add, ...)". Test showed that the new function is about three times faster than the original. Also it is shorter and easier to read. Signed-off-by: Andreas Florath <andreas@florath.net>
Signed-off-by: Andreas Florath <andreas@florath.net>
Signed-off-by: Andreas Florath <andreas@florath.net>
The setup and measurement was done in the same way as for #7593 Setting up a virtualenv with the original and optimized function side by side: def _getcount_orig(self):
"""Get total number of pixels in each layer"""
v = []
for i in range(0, len(self.h), 256):
v.append(functools.reduce(operator.add, self.h[i : i + 256]))
return v
def _getcount(self):
"""Get total number of pixels in each layer"""
return [sum(self.h[i: i + 256]) for i in range(0, len(self.h), 256)] Run the tests on the ImageNet dataset which revealed a speed improvement of about 3. Here is the adapted script which can be run using the Pillow test images: import pathlib
import timeit
from PIL import Image, ImageStat
IMAGEDIR="../Pillow/Tests/images"
testdir = pathlib.Path(IMAGEDIR)
NUMBER=10000
REPEAT=10
for image_file_name in testdir.rglob("*"):
# Skip broken images
try:
img = Image.open(image_file_name)
stat = ImageStat.Stat(img)
except Exception as ex:
continue
# Check for correctness
res_orig = stat._getcount_orig()
res_opt = stat._getcount()
assert res_orig == res_opt
# Measure improvement factor
exec_times_orig = timeit.repeat(
stmt=stat._getcount_orig, repeat=REPEAT, number=NUMBER)
exec_times_opt = timeit.repeat(
stmt=stat._getcount, repeat=REPEAT, number=NUMBER)
print("%10.4f - %s" % (
min(exec_times_orig) / min(exec_times_opt), image_file_name)) A typical output (partial):
The first number is the speedup-factor: the factor how much faster the proposed function is measured against the original. |
Hmmm. |
ImageStat.py is 100% covered: https://app.codecov.io/gh/python-pillow/Pillow/pull/7599/blob/src/PIL/ImageStat.py Coverage percentage can decrease if you delete covered lines. For example, imagine 3 covered out of 4 total lines = 75%. So we're fine for coverage :) |
Additionally, looking at the codecov report for the PR, most of the Windows jobs are missing coverage. I can see an error during the upload: https://github.com/python-pillow/Pillow/actions/runs/7084804763/job/19280550192?pr=7599#step:31:37
But it did not happen for all of the Windows jobs, pypy3.10 is fine. |
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! Nice to drop two imports as well :)
I've created #7605 to include this in the release notes. |
The optimized function improves the performance. The new implementation uses "sum" instead of the construct
"functools.reduce(operator.add, ...)". Tests showed that the new function is about three times faster than the original. Also it is shorter and easier to read and the dependency to functools and operator modules can be removed.
Changes proposed in this pull request: