It was pointed out to me that the benchmark wasn't all that fair. The python implementation didn't return a list of rects.
With that, Surface.blits takes 82%-85% of the time. Not 88%-92% as I stated earlier. In [4]: def blits(blit_list): ...: ret = [] ...: for surface, dest in blit_list: ...: ret.append(dst.blit(surface, dest)) ...: return ret ...: In [5]: In [5]: %timeit results = blits(blit_list) 841 µs ± 15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) In [6]: %timeit results = dst.blits(blit_list) 715 µs ± 15.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) In [7]: (100. / 841) * 717 Out[7]: 85.25564803804994 In [8]: (100. / 841) * 688 Out[8]: 81.80737217598097 On Tue, Apr 3, 2018 at 3:45 PM, René Dudfield <ren...@gmail.com> wrote: > Hellos, > > I made an initial implementation of Surface.blits(), focusing just on > correctness, with no optimizations. > https://github.com/pygame/pygame/pull/439 > > In the micro benchmark below it takes from 88% to 92% of the time for 255 > surfaces > compared to using Surface.blit in python in a loop over the same list. > > > .. method:: blits > > | :sl:`draw many images onto another` > | :sg:`blits(blit_sequence=(source, dest), ...), doreturn=1) -> > (Rect, ...)` > | :sg:`blits((source, dest, area), ...)) -> (Rect, ...)` > | :sg:`blits((source, dest, area, special_flags), ...)) -> (Rect, > ...)` > > Draws many surfaces onto this Surface. It takes a sequence as input, > with each of the elements corresponding to the ones of > ``Surface.blit()``. > It needs at minimum a sequence of (source, dest). > > > > Considering blit is usually the slow part in most pygame apps, this is > sort of nice. > > Note, I haven't updated pygame.sprite to use it. Volunteers? > I feel without updating pygame.sprite, many people won't use it. > > > Some benchmarking and other notes below. > > > cheers, > > > > > 1) Why not work on a faster implementation that saves the unwrapped > objects? > This would allow you to save a list into a C object like: > > struct blitinfo { > SDL_Surface dest; > GAME_Rect *src_rect; > GAME_Rect *area; > int flags; > } > > Then if you promise not to change the C list (ie, you are updating rects > in place, and all your Surfaces are still there), > then it could avoid a lot of the unwrapping work. > > However, I did a test where I commented out the blit call. So only the > unwrapping and looping over the list is done. > And it seems that the python book keeping for these 255 10x10 surfaces is > only 2.1%-3.3% of the total time taken. > > > > 2) Another optimization would be to avoid subsurface checks, and avoid a > few other preparations for surfaces. > I tried this, and didn't see any noticeable improvement. > > 3) Currently neither SDL1 or SDL2 have a special batched blit, but there > are proposals and implementations around. > Such as SDL_GPU. > This could see a bigger improvement on such backends where changing state > is slow (OpenGL etc). > > > > > > import pygame > from pygame.locals import * > NUM_SURFS = 255 > dst = pygame.Surface((NUM_SURFS * 10, 10), SRCALPHA, 32) > dst.fill((230, 230, 230)) > > blit_list = [] > for i in range(NUM_SURFS): > dest = (i * 10, 0) > surf = pygame.Surface((10, 10), SRCALPHA, 32) > color = (i * 1, i * 1, i * 1) > surf.fill(color) > blit_list.append((surf, dest)) > > def blits(blit_list): > for surface, dest in blit_list: > dst.blit(surface, dest) > > > > In [17]: %timeit results = blits(blit_list) > 774 µs ± 24.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) > > In [18]: %timeit results = dst.blits(blit_list) > 717 µs ± 12.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) > > In [19]: %timeit results = dst.blits(blit_list, doreturn=0) > 688 µs ± 14.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) > > In [20]: (100. / 774) * 717 > Out[20]: 92.63565891472868 > > In [21]: (100. / 774) * 688 > Out[21]: 88.88888888888889 > > > > If I comment out the actual blit call... > > In [3]: %timeit results = dst.blits(blit_list) > 26.2 µs ± 695 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) > > In [4]: %timeit results = dst.blits(blit_list, doreturn=0) > 17.6 µs ± 314 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) > > > In [5]: (100. / 774) * 26 > Out[5]: 3.3591731266149867 > > In [6]: (100. / 774) * 17 > Out[6]: 2.1963824289405682 > > > > > On Thu, Mar 30, 2017 at 7:12 PM, Leif Theden <leif.the...@gmail.com> > wrote: > >> Pull request sent with basic functionality of Surface.blits. >> >> https://bitbucket.org/pygame/pygame/pull-requests/79 >> >> On Thu, Mar 30, 2017 at 2:44 AM, René Dudfield <ren...@gmail.com> wrote: >> >>> Not sure. I need to do some benchmarking, and then more research into if >>> the API can cover everything we want it to do. (which of course means >>> figuring it out). So far I think the API I proposed will help with the >>> Groups we have, and will also help with people who don't care about dirty >>> rects. >>> >>> It's not really much code... as you can see Leif has already done >>> something like it... it's just a for loop around blit basically. >>> https://gist.github.com/bitcraft/1785face7c5684916cde >>> >>> If someone who just knows python wants to help, they could begin writing >>> unit tests for it in test/surface_test.py >>> >>> My pygame things are basically these at the moment. If anyone wants to >>> do the blits() API, that's fine too! >>> >>> * keep improving https://pygame.org/wiki/GettingStarted , gather and >>> file usability bugs with pip, python, pygame... >>> * help finalise github move decision/move. >>> * blits >>> * cython sprites.*Group >>> * SDL2 stuff >>> >>> >>> cheers, >>> >>> >>> >>> >>> On Thu, Mar 30, 2017 at 8:02 AM, DiliupG <dili...@gmail.com> wrote: >>> >>>> When will this fabulous upgrade be available? :) >>>> >>>> On 29 March 2017 at 12:58, René Dudfield <ren...@gmail.com> wrote: >>>> >>>>> On Wed, Mar 29, 2017 at 8:33 AM, Radomir Dopieralski < >>>>> pyg...@sheep.art.pl> wrote: >>>>> >>>>>> I think that special_flags could be shared for all the blits in the >>>>>> list. I can't think of a case where I would want to use different >>>>>> flags >>>>>> for each. >>>>>> >>>>>> >>>>> Yeah, I think you may be right. When I've used it (for particles as an >>>>> example) it was for everything in a Group. I searched through some code >>>>> online and found a few uses where it was used in Sprites. But then all of >>>>> those Sprites were rendered the same. There's some sort of nice symmetry >>>>> by >>>>> using the same arguments with blit, and blits however. >>>>> >>>> >>>> >>>> >>>> -- >>>> Kalasuri Diliup Gabadamudalige >>>> >>>> https://dahamgatalu.wordpress.com/ >>>> http://soft.diliupg.com/ >>>> http://www.diliupg.com >>>> >>>> ************************************************************ >>>> ********************************** >>>> This e-mail is confidential. It may also be legally privileged. If you >>>> are not the intended recipient or have received it in error, please delete >>>> it and all copies from your system and notify the sender immediately by >>>> return e-mail. Any unauthorized reading, reproducing, printing or further >>>> dissemination of this e-mail or its contents is strictly prohibited and may >>>> be unlawful. Internet communications cannot be guaranteed to be timely, >>>> secure, error or virus-free. The sender does not accept liability for any >>>> errors or omissions. >>>> ************************************************************ >>>> ********************************** >>>> >>>> >>> >> >