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

Add PyPy support #67

Closed
aclark4life opened this issue Mar 6, 2013 · 13 comments
Closed

Add PyPy support #67

aclark4life opened this issue Mar 6, 2013 · 13 comments

Comments

@aclark4life
Copy link
Member

Suggested by Maciej Fijalkowski on Twitter https://twitter.com/fijall/status/309321486815088640

@aclark4life
Copy link
Member Author

"the main idea would be to use cffi instead of C API, not sure if it's feasible" http://cffi.readthedocs.org/en/release-0.5/

@kmike
Copy link
Contributor

kmike commented Mar 7, 2013

Most of tests are already pass under PyPy (this means that C API used in Pillow is more or less compatible with cpyext):

3 tests of 82 failed

I also think Pillow could be already quite fast under PyPy because most of time is spent in C code doing image operations - wrapping/unwrapping objects shouldn't be a bottleneck for most Pillow use cases.

@fijal
Copy link

fijal commented Mar 7, 2013

This is not true. People all the time do stuff like write a list of values into image or manipulate it pixel by pixel and come later complaining that it's slow.

@cgohlke
Copy link
Contributor

cgohlke commented Mar 14, 2013

Looks good on [PyPy 1.9.0 with MSC v.1500 32 bit] on win32:

--------------------------------------------------------------------
PIL (Pillow) TEST SUMMARY
--------------------------------------------------------------------
Python modules loaded from .\PIL
Binary modules loaded from .
--------------------------------------------------------------------
--- PIL CORE support ok
*** TKINTER support not installed
--- JPEG support ok
--- ZLIB (PNG/ZIP) support ok
--- G4 TIFF support ok
--- FREETYPE2 support ok
--- LITTLECMS support ok
--- WEBP support ok
--------------------------------------------------------------------
Running selftest:
--- 57 tests passed.
Press any key to continue . . .
Setting environment for using Microsoft Visual Studio 2008 x86 tools.
--------------------------------------------------------------------
running test_000_sanity ...
running test_001_archive ...
running test_file_bmp ...
running test_file_fli ...
running test_file_gif ...
running test_file_ico ...
running test_file_jpeg ...
running test_file_msp ...
running test_file_pcx ...
running test_file_png ...
running test_file_ppm ...
running test_file_psd ...
running test_file_tar ...
running test_file_tiff ...
running test_file_xbm ...
running test_file_xpm ...
running test_font_bdf ...
running test_font_pcf ...
running test_image ...
running test_image_array ...
running test_image_convert ...
running test_image_copy ...
running test_image_crop ...
running test_image_draft ...
running test_image_filter ...
running test_image_frombytes ...
running test_image_getbands ...
running test_image_getbbox ...
running test_image_getcolors ...
running test_image_getdata ...
running test_image_getextrema ...
running test_image_getim ...
running test_image_getpalette ...
running test_image_getpixel ...
running test_image_getprojection ...
running test_image_histogram ...
running test_image_load ...
running test_image_mode ...
running test_image_offset ...
running test_image_paste ...
running test_image_point ...
running test_image_putalpha ...
running test_image_putdata ...
running test_image_putpalette ...
running test_image_putpixel ...
running test_image_quantize ...
running test_image_resize ...
running test_image_rotate ...
running test_image_save ...
running test_image_seek ...
running test_image_show ...
running test_image_split ...
running test_image_tell ...
running test_image_thumbnail ...
running test_image_tobitmap ...
running test_image_tobytes ...
running test_image_transform ...
running test_image_transpose ...
running test_image_verify ...
running test_imagechops ...
running test_imagecms ...
running test_imagecolor ...
running test_imagedraw ...
running test_imageenhance ...
running test_imagefile ...
running test_imagefileio ...
running test_imagefilter ...
running test_imagefont ...
running test_imagegl ...
--- skipped
running test_imagegrab ...
running test_imagemath ...
running test_imagemode ...
running test_imageops ...
running test_imageops_usm ...
running test_imagepalette ...
running test_imagepath ...
running test_imageqt ...
--- skipped
running test_imagesequence ...
running test_imageshow ...
running test_imagestat ...
running test_imagetk ...
--- skipped
running test_imagetransform ...
running test_imagewin ...
running test_lib_image ...
running test_lib_pack ...
running test_mode_i16 ...
running test_numpy ...
--- skipped
--------------------------------------------------------------------
=== remaining temporary files
Tests\temp_file_bmp_0.bmp
Tests\temp_file_jpeg_0.jpg
Tests\temp_file_msp_0.msp
Tests\temp_file_pcx_0.pcx
Tests\temp_file_png_0.png
Tests\temp_file_tiff_0.tif
Tests\temp_font_pcf_0.pbm
Tests\temp_font_pcf_0.pil
Tests\temp_imagepalette_0.lut
Tests\temp_image_split_0.png
Tests\temp_mode_i16_1.im
Tests\temp_mode_i16_2.im
--------------------------------------------------------------------
--- 4 tests skipped.
['test_imagegl', 'test_imageqt', 'test_imagetk', 'test_numpy']
83 tests passed.

@aclark4life
Copy link
Member Author

Wow

@slackydev
Copy link

I've done a lot of testing, the biggest problem I found is that the "pixel access object" is simply to slow under PyPy, close to useless. The workaround I found was to use the raw pixeldata (as string, array or list), and then change/read the pixels, then convert it back to a PIL-image again (image.fromstring). This was quite fast, actually faster than CPython in many cases, but it's a really clumsy solution, using struct/or array module...

An example of iterating over 1000x1000 pixels, and getting the pixelval of each pixel:

from PIL import Image

def py_optimal(im):
    W,H = im.size
    pix = im.load()
    for x in range(W):
        for y in xrange(H):
            F = pix[x,y]
            # do something...
from PIL import Image
from array import array

def pypy_optimal(im):
    W,H = im.size
    pix = array('B', im.convert("RGB").tostring('raw'))
    i = 0
    for x in range(W):
        for y in range(H):
            r = pix[i+0]
            g = pix[i+1]
            b = pix[i+2]
            i += 3
            # do something..

So i tested these two ways of READING the pixels 50 times to get an accurate timing...

The first solutions (standard way to do it), time used was:

PyPy 1.9:
8.57063293457 sec. (far to slow!!)
CPython 2.7:
0.164180994034 sec. (164 ms)

The workaround I use: Time:

PyPy 1.9:
0.0398070812225 sec. (40 ms)
CPython 2.7:
0.333108901978 sec.

You should really optimizing this for PyPy!

@aclark4life
Copy link
Member Author

Thanks for the comment, you are welcome to send a pull request

@wiredfool
Copy link
Member

PyPy is kind of a moving target for optimization purposes. (there's a 2.0 out now).

I'm guessing that the issue is crossing the c/python boundary inside a tight loop, and building a return value each time. It's likely that the c extension in C python has a much lower impedance mismatch.

@fijal
Copy link

fijal commented May 20, 2013

On Mon, May 20, 2013 at 10:48 PM, wiredfool notifications@git.luolix.topwrote:

PyPy is kind of a moving target for optimization purposes. (there's a 2.0
out now).

I'm guessing that the issue is crossing the c/python boundary inside a
tight loop, and building a return value each time. It's likely that the c
extension in C python has a much lower impedance mismatch.


Reply to this email directly or view it on GitHubhttps://github.com//issues/67#issuecomment-18172339
.

CPython C API is a compatibility layer that is and always will be very
slow. It has absolutely nothing to do with a moving target (is pypy really
a moving target?), it's just that we provide an API so non-native that it
hurts.

@wiredfool
Copy link
Member

A moving target = quickly progressing.

The pixel access object is pretty simple. My suspicion is that if it was pure python, it might be slow in CPython, but probably fast in PyPy. As it is, it's the opposite as a c api object.

Unfortunately, the pixel_access_object is only a thin shim on the Access.c get_pixel/put_pixel methods, and I don't see a way to get to the pixel data from python, without dropping to C somewhere. There might be a way with buffer objects, but I'm not sure that the image stores it's data in a way appropriate for that.

@fijal
Copy link

fijal commented May 20, 2013

On Mon, May 20, 2013 at 11:28 PM, wiredfool notifications@git.luolix.topwrote:

A moving target = quickly progressing.

The pixel access object is pretty simple. My suspicion is that if it was
pure python, it might be slow in CPython, but probably fast in PyPy. As it
is, it's the opposite as a c api object.

Unfortunately, the pixel_access_object is only a thin shim on the Access.c
get_pixel/put_pixel methods, and I don't see a way to get to the pixel data
from python, without dropping to C somewhere. There might be a way with
buffer objects, but I'm not sure that the image stores it's data in a way
appropriate for that.


Reply to this email directly or view it on GitHubhttps://github.com//issues/67#issuecomment-18175276
.

If you need to call, call it with cffi (C functions directly) instead of C
API. That would be much better and should be reasonably fast on pypy. Pure
python is obviously better (you avoid function calls), but cffi is
definitely next good, if the image does not export any way to get to the
raw data.

@fijal
Copy link

fijal commented May 20, 2013

On Tue, May 21, 2013 at 12:12 AM, Maciej Fijalkowski fijall@gmail.comwrote:

On Mon, May 20, 2013 at 11:28 PM, wiredfool notifications@git.luolix.topwrote:

A moving target = quickly progressing.

The pixel access object is pretty simple. My suspicion is that if it was
pure python, it might be slow in CPython, but probably fast in PyPy. As it
is, it's the opposite as a c api object.

Unfortunately, the pixel_access_object is only a thin shim on the
Access.c get_pixel/put_pixel methods, and I don't see a way to get to the
pixel data from python, without dropping to C somewhere. There might be a
way with buffer objects, but I'm not sure that the image stores it's data
in a way appropriate for that.


Reply to this email directly or view it on GitHubhttps://github.com//issues/67#issuecomment-18175276
.

If you need to call, call it with cffi (C functions directly) instead of C
API. That would be much better and should be reasonably fast on pypy. Pure
python is obviously better (you avoid function calls), but cffi is
definitely next good, if the image does not export any way to get to the
raw data.

to be perfectly clear - I doubt there is no way to access raw data one
way or another. The ideal way would be to get the raw data pointer and
access it via cffi.

@wiredfool
Copy link
Member

No is probably an incorrect term. But from my quick reading of the code, it appears that the image may be stored in multiple chunks, which would complicate access from just the pointer.

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

No branches or pull requests

6 participants