Why is a piglet so slow compared to pygame?

Here is the code. 5000 hopping spinning red squares. (16x16 png) In the pygame version, I get 30 frames per second, but 10 frames per second with a piglet. Should OpenGl be faster for this kind of thing?

pygame version:

import pygame, sys, random from pygame.locals import * import cProfile # Set FPS FPS = 60.0 clock = pygame.time.Clock() # Set window WINDOWWIDTH= 800 WINDOWHEIGHT = 600 pygame.init() screen = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT)) screen.fill((0,0,0)) background = screen.copy().convert() image = pygame.image.load("square.png").convert() class Square(object): def __init__(self,x,y): self.x = x self.y = y self.v_x = random.randint(1,100) self.v_y = random.randint(1,100) self.v_r = random.randint(-100,100) self.rotation = 0 def __rep__(self): return "Square %d,%d"%(self.x,self.y) def update(self,dt): if self.x > WINDOWWIDTH: self.v_x *= -1 elif self.x < 0: self.v_x *= -1 if self.y > WINDOWHEIGHT: self.v_y *= -1 elif self.y < 0: self.v_y *= -1 self.x += self.v_x * dt self.y += self.v_y * dt self.rotation += self.v_r * dt def draw(self): screen.blit(pygame.transform.rotate(image,self.rotation),(self.x,self.y)) sqrs = [] for _ in range(5000): sqrs.append( Square(random.randint(0,WINDOWWIDTH-1),random.randint(0,WINDOWHEIGHT-1)) ) def main_loop(): tick = 0.0 elapsed = 0.0 while elapsed < 10.0: dt = tick/1000.0 # Events for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() # Logic for s in sqrs: s.update(dt) # Drawing screen.blit(background,(0,0)) for s in sqrs: s.draw() pygame.display.update() pygame.display.set_caption('test program FPS: %s'%(clock.get_fps() ) ) tick = clock.tick(FPS) elapsed += tick/1000.0 pygame.quit() cProfile.run("main_loop()") i = input("...") 

pyglet version:

 import cProfile import pyglet, random # Disable error checking for increased performance pyglet.options['debug_gl'] = False from pyglet import clock clock.set_fps_limit(60) WINDOWWIDTH = 800 WINDOWHEIGHT = 600 FPS = 60.0 batch = pyglet.graphics.Batch() window = pyglet.window.Window(WINDOWWIDTH,WINDOWHEIGHT) fps_display = pyglet.clock.ClockDisplay() image = pyglet.resource.image("square.png") class Square(pyglet.sprite.Sprite): def __init__(self,x,y): pyglet.sprite.Sprite.__init__(self,img = image,batch=batch) self.x = x self.y = y self.v_x = random.randint(1,100) self.v_y = random.randint(1,100) self.v_r = random.randint(-100,100) def update(self,dt): if self.x > WINDOWWIDTH: self.v_x *= -1 elif self.x < 0: self.v_x *= -1 if self.y > WINDOWHEIGHT: self.v_y *= -1 elif self.y < 0: self.v_y *= -1 self.x += self.v_x * dt self.y += self.v_y * dt self.rotation += self.v_r * dt sqrs = [] for _ in range(5000): sqrs.append( Square(random.randint(0,WINDOWWIDTH-1),random.randint(0,WINDOWHEIGHT-1)) ) elapsed = 0.0 def update(dt): global elapsed elapsed += dt if elapsed >= 10.0: clock.unschedule(update) window.close() else: for s in sqrs: s.update(dt) @window.event def on_draw(): window.clear() batch.draw() fps_display.draw() clock.schedule_interval(update, 1.0/FPS) if __name__ == '__main__': cProfile.run("pyglet.app.run()") c = input("...") 

CProfile result for pygame:

  5341607 function calls in 9.429 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 9.429 9.429 <string>:1(<module>) 1335000 2.259 0.000 2.259 0.000 pygame-test.py:32(update) 1335000 1.323 0.000 5.969 0.000 pygame-test.py:46(draw) 1 0.772 0.772 9.429 9.429 pygame-test.py:55(main_loop) 1 0.000 0.000 9.429 9.429 {built-in method exec} 267 0.020 0.000 0.020 0.000 {built-in method get} 1 0.237 0.237 0.237 0.237 {built-in method quit} 1335000 3.479 0.000 3.479 0.000 {built-in method rotate} 267 0.013 0.000 0.013 0.000 {built-in method set_caption} 267 0.067 0.000 0.067 0.000 {built-in method update} 1335267 1.257 0.000 1.257 0.000 {method 'blit' of 'pygame.Surface' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 267 0.000 0.000 0.000 0.000 {method 'get_fps' of 'Clock' objects} 267 0.001 0.000 0.001 0.000 {method 'tick' of 'Clock' objects} 

Pyglet cProfile output: - Very long, this is partial output, the full version is here .

  9982775 function calls (9982587 primitive calls) in 10.066 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 123 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:1596(_handle_fromlist) 1 0.000 0.000 10.067 10.067 <string>:1(<module>) 11 0.000 0.000 0.000 0.000 __init__.py:1055(_ensure_string_data) 11 0.000 0.000 0.000 0.000 __init__.py:1061(_get_gl_format_and_type) 58 0.000 0.000 0.000 0.000 __init__.py:1140(clear) 75 0.000 0.000 0.012 0.000 __init__.py:1148(dispatch_event) 1 0.000 0.000 10.067 10.067 __init__.py:115(run) ... 1 0.000 0.000 0.000 0.000 lib.py:124(decorate_function) 1108 0.005 0.000 0.005 0.000 lib_wgl.py:80(__call__) 285000 1.409 0.000 9.872 0.000 pyglet-test.py:29(update) 58 0.105 0.002 9.982 0.172 pyglet-test.py:49(update) ... 855000 5.436 0.000 7.551 0.000 sprite.py:378(_update_position) 285000 0.172 0.000 2.718 0.000 sprite.py:441(_set_x) 851800 0.177 0.000 0.177 0.000 sprite.py:445(<lambda>) 285000 0.174 0.000 2.670 0.000 sprite.py:451(_set_y) 851115 0.155 0.000 0.155 0.000 sprite.py:455(<lambda>) 285000 0.182 0.000 2.692 0.000 sprite.py:461(_set_rotation) 285000 0.051 0.000 0.051 0.000 sprite.py:465(<lambda>) ... 4299 0.007 0.000 0.025 0.000 vertexattribute.py:308(get_region) 1 0.000 0.000 0.000 0.000 vertexattribute.py:380(__init__) 116 0.000 0.000 0.000 0.000 vertexattribute.py:384(enable) 116 0.000 0.000 0.000 0.000 vertexattribute.py:387(set_pointer) 1 0.000 0.000 0.000 0.000 vertexattribute.py:461(__init__) 116 0.000 0.000 0.000 0.000 vertexattribute.py:466(enable) 116 0.000 0.000 0.000 0.000 vertexattribute.py:469(set_pointer) 1 0.000 0.000 0.000 0.000 vertexattribute.py:501(__init__) 116 0.000 0.000 0.000 0.000 vertexattribute.py:508(enable) 116 0.000 0.000 0.000 0.000 vertexattribute.py:511(set_pointer) 3 0.000 0.000 0.000 0.000 vertexbuffer.py:293(__init__) 348 0.000 0.000 0.001 0.000 vertexbuffer.py:311(bind) 348 0.000 0.000 0.001 0.000 vertexbuffer.py:314(unbind) 3 0.000 0.000 0.000 0.000 vertexbuffer.py:381(__init__) 348 0.001 0.000 0.004 0.000 vertexbuffer.py:388(bind) 4299 0.006 0.000 0.016 0.000 vertexbuffer.py:420(get_region) 3 0.000 0.000 0.000 0.000 vertexbuffer.py:424(resize) 4299 0.002 0.000 0.002 0.000 vertexbuffer.py:460(__init__) 855232 0.735 0.000 1.053 0.000 vertexbuffer.py:466(invalidate) ... 855058 0.687 0.000 1.762 0.000 vertexdomain.py:581(_get_vertices) ... 4300 0.002 0.000 0.002 0.000 {built-in method POINTER} ... 841451 0.162 0.000 0.162 0.000 {built-in method cos} ... 2417/2415 0.000 0.000 0.000 0.000 {built-in method len} 855489 0.142 0.000 0.142 0.000 {built-in method max} 855469 0.176 0.000 0.176 0.000 {built-in method min} 465/407 0.000 0.000 0.000 0.000 {built-in method next} ... 841451 0.072 0.000 0.072 0.000 {built-in method radians} 62 0.000 0.000 0.000 0.000 {built-in method setattr} 841451 0.120 0.000 0.120 0.000 {built-in method sin} ... 
+7
pygame pyglet
source share
2 answers

The bottleneck is in the rotor of the pirlet. If you comment on the line "self.rotation" in the Square update () method, your fps will almost double.

+2
source share

Piglet images (and sprites) can rotate around an arbitrary anchor. If you look at pyglet/sprite.py , you will see that this is done using the Python math module, so it is rather slow. It would seem that there is room for optimization here by rotating sprites using OpenGL glRotate or even a vertex shader.

+2
source share

All Articles