From 75dac32a0a1c6755e41735c9098d4932c001e405 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 23 Jul 2014 14:30:55 -0700 Subject: [PATCH 01/10] single threaded for profile-installed.py --- profile-installed.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/profile-installed.py b/profile-installed.py index 485792f51..be9a960d2 100755 --- a/profile-installed.py +++ b/profile-installed.py @@ -21,14 +21,6 @@ if len(sys.argv) == 1: # Make sure that nose doesn't muck with our paths. if ('--no-path-adjustment' not in sys.argv) and ('-P' not in sys.argv): sys.argv.insert(1, '--no-path-adjustment') - -if 'NOSE_PROCESSES' not in os.environ: - for arg in sys.argv: - if '--processes' in arg: - break - else: # for - sys.argv.insert(1, '--processes=-1') # -1 == number of cores - sys.argv.insert(1, '--process-timeout=30') if __name__ == '__main__': profile.run("nose.main()", sort=2) From 94ca2b10763bd6ed127fba85b976dff703a64f3b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 23 Jul 2014 14:31:49 -0700 Subject: [PATCH 02/10] using skip known bad --- Tests/helper.py | 5 ++++- Tests/test_image_point.py | 11 ++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 082fb93f9..3f7913b11 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -124,7 +124,8 @@ class PillowTestCase(unittest.TestCase): self.assertTrue(found) return result - def skipKnownBadTest(self, msg=None, platform=None, travis=None): + def skipKnownBadTest(self, msg=None, platform=None, + travis=None, interpreter=None): # Skip if platform/travis matches, and # PILLOW_RUN_KNOWN_BAD is not true in the environment. if bool(os.environ.get('PILLOW_RUN_KNOWN_BAD', False)): @@ -136,6 +137,8 @@ class PillowTestCase(unittest.TestCase): skip = sys.platform.startswith(platform) if travis is not None: skip = skip and (travis == bool(os.environ.get('TRAVIS', False))) + if interpreter is not None: + skip = skip and (interpreter == 'pypy' and hasattr(sys, 'pypy_version_info')) if skip: self.skipTest(msg or "Known Bad Test") diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 7b6cd4fc7..aa134fcc6 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -5,12 +5,6 @@ import sys class TestImagePoint(PillowTestCase): - def setUp(self): - if hasattr(sys, 'pypy_version_info'): - # This takes _forever_ on PyPy. Open Bug, - # see https://github.com/python-pillow/Pillow/issues/484 - self.skipTest("Too slow on PyPy") - def test_sanity(self): im = lena() @@ -29,7 +23,10 @@ class TestImagePoint(PillowTestCase): def test_16bit_lut(self): """ Tests for 16 bit -> 8 bit lut for converting I->L images see https://github.com/python-pillow/Pillow/issues/440 - """ + """ + # This takes _forever_ on PyPy. Open Bug, + # see https://github.com/python-pillow/Pillow/issues/484 + self.skipKnownBadTest(msg="Too Slow on pypy", interpreter='pypy') im = lena("I") im.point(list(range(256))*256, 'L') From a5aea42bc9d82a351d75e0e73f52f294cb48039b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 23 Jul 2014 14:37:04 -0700 Subject: [PATCH 03/10] Use PySequence_Fast* to iterate over the list Pre commit: $ NOSE_PROCESSES=0 PILLOW_RUN_KNOWN_BAD=1 time ./test-installed.py Tests/test_image_point.py .. ---------------------------------------------------------------------- Ran 2 tests in 132.056s OK 131.63user 0.62system 2:12.28elapsed 99%CPU (0avgtext+0avgdata 292176maxresident)k 264inputs+0outputs (2major+451088minor)pagefaults 0swaps Post: $ NOSE_PROCESSES=0 PILLOW_RUN_KNOWN_BAD=1 time ./test-installed.py Tests/test_image_point.py .. ---------------------------------------------------------------------- Ran 2 tests in 0.338s OK 0.52user 0.06system 0:00.59elapsed 98%CPU (0avgtext+0avgdata 257584maxresident)k 176inputs+32outputs (2major+18033minor)pagefaults 0swaps $ python --version Python 2.7.6 (2.3.1+dfsg-1~ppa1, Jun 20 2014, 09:27:47) [PyPy 2.3.1 with GCC 4.6.3] --- _imaging.c | 86 +++++++++++++++++++----------------------------------- 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/_imaging.c b/_imaging.c index 92258032f..7d0bcba04 100644 --- a/_imaging.c +++ b/_imaging.c @@ -365,9 +365,12 @@ getbands(const char* mode) static void* getlist(PyObject* arg, int* length, const char* wrong_length, int type) { - int i, n; + int i, n, itemp; + double dtemp; void* list; - + PyObject* seq; + PyObject* op; + if (!PySequence_Check(arg)) { PyErr_SetString(PyExc_TypeError, must_be_sequence); return NULL; @@ -383,70 +386,41 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type) if (!list) return PyErr_NoMemory(); + seq = PySequence_Fast(arg, must_be_sequence); + if (!seq) { + free(list); + PyErr_SetString(PyExc_TypeError, must_be_sequence); + return NULL; + } + switch (type) { case TYPE_UINT8: - if (PyList_Check(arg)) { - for (i = 0; i < n; i++) { - PyObject *op = PyList_GET_ITEM(arg, i); - int temp = PyInt_AsLong(op); - ((UINT8*)list)[i] = CLIP(temp); - } - } else { - for (i = 0; i < n; i++) { - PyObject *op = PySequence_GetItem(arg, i); - int temp = PyInt_AsLong(op); - Py_XDECREF(op); - ((UINT8*)list)[i] = CLIP(temp); - } + for (i = 0; i < n; i++) { + op = PySequence_Fast_GET_ITEM(seq, i); + itemp = PyInt_AsLong(op); + ((UINT8*)list)[i] = CLIP(itemp); } break; case TYPE_INT32: - if (PyList_Check(arg)) { - for (i = 0; i < n; i++) { - PyObject *op = PyList_GET_ITEM(arg, i); - int temp = PyInt_AsLong(op); - ((INT32*)list)[i] = temp; - } - } else { - for (i = 0; i < n; i++) { - PyObject *op = PySequence_GetItem(arg, i); - int temp = PyInt_AsLong(op); - Py_XDECREF(op); - ((INT32*)list)[i] = temp; - } + for (i = 0; i < n; i++) { + op = PySequence_Fast_GET_ITEM(seq, i); + itemp = PyInt_AsLong(op); + ((INT32*)list)[i] = itemp; } break; case TYPE_FLOAT32: - if (PyList_Check(arg)) { - for (i = 0; i < n; i++) { - PyObject *op = PyList_GET_ITEM(arg, i); - double temp = PyFloat_AsDouble(op); - ((FLOAT32*)list)[i] = (FLOAT32) temp; - } - } else { - for (i = 0; i < n; i++) { - PyObject *op = PySequence_GetItem(arg, i); - double temp = PyFloat_AsDouble(op); - Py_XDECREF(op); - ((FLOAT32*)list)[i] = (FLOAT32) temp; - } - } + for (i = 0; i < n; i++) { + op = PySequence_Fast_GET_ITEM(seq, i); + dtemp = PyFloat_AsDouble(op); + ((FLOAT32*)list)[i] = (FLOAT32) dtemp; + } break; case TYPE_DOUBLE: - if (PyList_Check(arg)) { - for (i = 0; i < n; i++) { - PyObject *op = PyList_GET_ITEM(arg, i); - double temp = PyFloat_AsDouble(op); - ((double*)list)[i] = temp; - } - } else { - for (i = 0; i < n; i++) { - PyObject *op = PySequence_GetItem(arg, i); - double temp = PyFloat_AsDouble(op); - Py_XDECREF(op); - ((double*)list)[i] = temp; - } - } + for (i = 0; i < n; i++) { + op = PySequence_Fast_GET_ITEM(seq, i); + dtemp = PyFloat_AsDouble(op); + ((double*)list)[i] = (double) dtemp; + } break; } From 5def1010c72bde10cebc2aafbb7cf6a30ae070c7 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 23 Jul 2014 15:16:23 -0700 Subject: [PATCH 04/10] DRY, moved case inside loop --- _imaging.c | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/_imaging.c b/_imaging.c index 7d0bcba04..7c55f633b 100644 --- a/_imaging.c +++ b/_imaging.c @@ -393,35 +393,28 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type) return NULL; } - switch (type) { - case TYPE_UINT8: - for (i = 0; i < n; i++) { - op = PySequence_Fast_GET_ITEM(seq, i); + for (i = 0; i < n; i++) { + op = PySequence_Fast_GET_ITEM(seq, i); + // DRY, branch prediction is going to work _really_ well + // on this switch. And 3 fewer loops to copy/paste. + switch (type) { + case TYPE_UINT8: itemp = PyInt_AsLong(op); ((UINT8*)list)[i] = CLIP(itemp); - } - break; - case TYPE_INT32: - for (i = 0; i < n; i++) { - op = PySequence_Fast_GET_ITEM(seq, i); + break; + case TYPE_INT32: itemp = PyInt_AsLong(op); ((INT32*)list)[i] = itemp; - } - break; - case TYPE_FLOAT32: - for (i = 0; i < n; i++) { - op = PySequence_Fast_GET_ITEM(seq, i); - dtemp = PyFloat_AsDouble(op); - ((FLOAT32*)list)[i] = (FLOAT32) dtemp; - } - break; - case TYPE_DOUBLE: - for (i = 0; i < n; i++) { - op = PySequence_Fast_GET_ITEM(seq, i); + break; + case TYPE_FLOAT32: + dtemp = PyFloat_AsDouble(op); + ((FLOAT32*)list)[i] = (FLOAT32) dtemp; + break; + case TYPE_DOUBLE: dtemp = PyFloat_AsDouble(op); ((double*)list)[i] = (double) dtemp; - } - break; + break; + } } if (length) From 06d21bc709e80ffb97429ed85dde5530d3888526 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 23 Jul 2014 15:29:10 -0700 Subject: [PATCH 05/10] pypy performance test --- Tests/test_image_putdata.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index c7c3115aa..036ee285f 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -42,6 +42,10 @@ class TestImagePutData(PillowTestCase): self.assertEqual(put(sys.maxsize), (255, 255, 255, 127)) + def test_pypy_performance(self): + im = Image.new('L', (256,256)) + im.putdata(list(range(256))*256) + if __name__ == '__main__': unittest.main() From c9c80f9da5fe120181deb970e43efef903156bda Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 23 Jul 2014 15:38:11 -0700 Subject: [PATCH 06/10] Use PySequence_Fast for Image.putdata Pre-Commit: $ NOSE_PROCESSES=0 time ./test-installed.py Tests/test_image_putdata.py ... ---------------------------------------------------------------------- Ran 3 tests in 131.623s OK 131.77user 0.18system 2:14.04elapsed 98%CPU (0avgtext+0avgdata 325632maxresident)k 87376inputs+8outputs (314major+47333minor)pagefaults 0swaps Post: $ NOSE_PROCESSES=0 time ./test-installed.py Tests/test_image_putdata.py ... ---------------------------------------------------------------------- Ran 3 tests in 0.534s OK 0.77user 0.05system 0:00.83elapsed 99%CPU (0avgtext+0avgdata 296352maxresident)k 0inputs+0outputs (0major+21462minor)pagefaults 0swaps --- _imaging.c | 91 +++++++++++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/_imaging.c b/_imaging.c index 7c55f633b..c28bd4d93 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1220,6 +1220,8 @@ _putdata(ImagingObject* self, PyObject* args) Py_ssize_t n, i, x, y; PyObject* data; + PyObject* seq; + PyObject* op; double scale = 1.0; double offset = 0.0; @@ -1259,69 +1261,61 @@ _putdata(ImagingObject* self, PyObject* args) x = 0, y++; } } else { - if (scale == 1.0 && offset == 0.0) { - /* Clipped data */ - if (PyList_Check(data)) { - for (i = x = y = 0; i < n; i++) { - PyObject *op = PyList_GET_ITEM(data, i); - image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op)); - if (++x >= (int) image->xsize) - x = 0, y++; - } - } else { - for (i = x = y = 0; i < n; i++) { - PyObject *op = PySequence_GetItem(data, i); - image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op)); - Py_XDECREF(op); - if (++x >= (int) image->xsize) - x = 0, y++; - } - } + seq = PySequence_Fast(data, must_be_sequence); + if (!seq) { + PyErr_SetString(PyExc_TypeError, must_be_sequence); + return NULL; + } + if (scale == 1.0 && offset == 0.0) { + /* Clipped data */ + for (i = x = y = 0; i < n; i++) { + op = PySequence_Fast_GET_ITEM(data, i); + image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op)); + if (++x >= (int) image->xsize){ + x = 0, y++; + } + } + } else { - if (PyList_Check(data)) { - /* Scaled and clipped data */ - for (i = x = y = 0; i < n; i++) { - PyObject *op = PyList_GET_ITEM(data, i); - image->image8[y][x] = CLIP( - (int) (PyFloat_AsDouble(op) * scale + offset)); - if (++x >= (int) image->xsize) - x = 0, y++; - } - } else { - for (i = x = y = 0; i < n; i++) { - PyObject *op = PySequence_GetItem(data, i); - image->image8[y][x] = CLIP( - (int) (PyFloat_AsDouble(op) * scale + offset)); - Py_XDECREF(op); - if (++x >= (int) image->xsize) - x = 0, y++; - } - } - } - PyErr_Clear(); /* Avoid weird exceptions */ + /* Scaled and clipped data */ + for (i = x = y = 0; i < n; i++) { + PyObject *op = PySequence_Fast_GET_ITEM(data, i); + image->image8[y][x] = CLIP( + (int) (PyFloat_AsDouble(op) * scale + offset)); + if (++x >= (int) image->xsize){ + x = 0, y++; + } + } + } + PyErr_Clear(); /* Avoid weird exceptions */ } } else { /* 32-bit images */ + seq = PySequence_Fast(data, must_be_sequence); + if (!seq) { + PyErr_SetString(PyExc_TypeError, must_be_sequence); + return NULL; + } switch (image->type) { case IMAGING_TYPE_INT32: for (i = x = y = 0; i < n; i++) { - PyObject *op = PySequence_GetItem(data, i); + op = PySequence_Fast_GET_ITEM(data, i); IMAGING_PIXEL_INT32(image, x, y) = (INT32) (PyFloat_AsDouble(op) * scale + offset); - Py_XDECREF(op); - if (++x >= (int) image->xsize) + if (++x >= (int) image->xsize){ x = 0, y++; + } } PyErr_Clear(); /* Avoid weird exceptions */ break; case IMAGING_TYPE_FLOAT32: for (i = x = y = 0; i < n; i++) { - PyObject *op = PySequence_GetItem(data, i); + op = PySequence_Fast_GET_ITEM(data, i); IMAGING_PIXEL_FLOAT32(image, x, y) = (FLOAT32) (PyFloat_AsDouble(op) * scale + offset); - Py_XDECREF(op); - if (++x >= (int) image->xsize) + if (++x >= (int) image->xsize){ x = 0, y++; + } } PyErr_Clear(); /* Avoid weird exceptions */ break; @@ -1332,16 +1326,15 @@ _putdata(ImagingObject* self, PyObject* args) INT32 inkint; } u; - PyObject *op = PySequence_GetItem(data, i); + op = PySequence_Fast_GET_ITEM(data, i); if (!op || !getink(op, image, u.ink)) { - Py_DECREF(op); return NULL; } /* FIXME: what about scale and offset? */ image->image32[y][x] = u.inkint; - Py_XDECREF(op); - if (++x >= (int) image->xsize) + if (++x >= (int) image->xsize){ x = 0, y++; + } } PyErr_Clear(); /* Avoid weird exceptions */ break; From 2d13dbda6a3d9ddc8ac93e839162353bb7d9811d Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 23 Jul 2014 16:01:06 -0700 Subject: [PATCH 07/10] enable test_16bit_lut on pypy --- Tests/test_image_point.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index aa134fcc6..63fb6fc38 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -26,7 +26,7 @@ class TestImagePoint(PillowTestCase): """ # This takes _forever_ on PyPy. Open Bug, # see https://github.com/python-pillow/Pillow/issues/484 - self.skipKnownBadTest(msg="Too Slow on pypy", interpreter='pypy') + #self.skipKnownBadTest(msg="Too Slow on pypy", interpreter='pypy') im = lena("I") im.point(list(range(256))*256, 'L') From ea0a31d9fe9b4088423bebb3ba259098268e2102 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 23 Jul 2014 17:02:57 -0700 Subject: [PATCH 08/10] 2.8 million pyaccesses take a while, nomatter what --- Tests/test_mode_i16.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index b7dc76fb4..0cd5dba0f 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -5,18 +5,22 @@ from PIL import Image class TestModeI16(PillowTestCase): + original = lena().resize((32,32)).convert('I') + def verify(self, im1): - im2 = lena("I") + im2 = self.original.copy() self.assertEqual(im1.size, im2.size) pix1 = im1.load() pix2 = im2.load() for y in range(im1.size[1]): for x in range(im1.size[0]): xy = x, y + p1 = pix1[xy] + p2 = pix2[xy] self.assertEqual( - pix1[xy], pix2[xy], + p1, p2, ("got %r from mode %s at %s, expected %r" % - (pix1[xy], im1.mode, xy, pix2[xy]))) + (p1, im1.mode, xy, p2))) def test_basic(self): # PIL 1.1 has limited support for 16-bit image data. Check that @@ -24,7 +28,7 @@ class TestModeI16(PillowTestCase): def basic(mode): - imIn = lena("I").convert(mode) + imIn = self.original.convert(mode) self.verify(imIn) w, h = imIn.size @@ -92,7 +96,7 @@ class TestModeI16(PillowTestCase): def test_convert(self): - im = lena("I") + im = self.original.copy() self.verify(im.convert("I;16")) self.verify(im.convert("I;16").convert("L")) From 78d26180647956b41372800eff223a3b5ca871d6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 28 Jul 2014 21:49:11 -0700 Subject: [PATCH 09/10] Image.point tests for Float LUT --- Tests/test_image_point.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 63fb6fc38..04054fa84 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -31,6 +31,16 @@ class TestImagePoint(PillowTestCase): im = lena("I") im.point(list(range(256))*256, 'L') + def test_f_lut(self): + """ Tests for floating point lut of 8bit gray image """ + im = lena('L') + lut = [0.5 * float(x) for x in range(256)] + + out = im.point(lut, 'F') + + int_lut = [x//2 for x in range(256)] + self.assert_image_equal(out.convert('L'), im.point(int_lut, 'L')) + if __name__ == '__main__': unittest.main() From 1a245a577bcc5a0fb2b73e0d2ca2b6827b31274e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 28 Jul 2014 22:09:52 -0700 Subject: [PATCH 10/10] Mode F and I tests for Image.putdata --- Tests/test_image_putdata.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index 036ee285f..acea0d62a 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -46,6 +46,26 @@ class TestImagePutData(PillowTestCase): im = Image.new('L', (256,256)) im.putdata(list(range(256))*256) + def test_mode_i(self): + src = lena('L') + data = list(src.getdata()) + im = Image.new('I', src.size, 0) + im.putdata(data, 2, 256) + + target = [2* elt + 256 for elt in data] + self.assertEqual(list(im.getdata()), target) + + def test_mode_F(self): + src = lena('L') + data = list(src.getdata()) + im = Image.new('F', src.size, 0) + im.putdata(data, 2.0, 256.0) + + target = [2.0* float(elt) + 256.0 for elt in data] + self.assertEqual(list(im.getdata()), target) + + + if __name__ == '__main__': unittest.main()