Hi,

Thanks for the quick feedback, and apologies if my little program caused
some confusion - I wrote it quickly to highlight an issue, but I guess I
should have included some results from another machine for comparison.

I made some improvements based on Ian's suggestions:

1. I don't average anything, just output the recorded fps values
2. I removed the argument to tick() - this actually helps to showcase the
issue

I ran the code (pasted below) on two machines:

Machine A:
Intel(R) Core(TM) i7-5930K CPU @ 3.50GHz
Python 3.7, Pygame1.9.6

Machine B:
Intel(R) Core(TM) i7-4650U CPU @ 1.70GHz
Python 3.7, Pygame1.9.6

The first benchmark on google results shows A should be significantly
faster than B
https://cpu.userbenchmark.com/Compare/Intel-Core-i7-4650U-vs-Intel-Core-i7-5930K/m6364vs2578

I ran pyperformance (
https://pyperformance.readthedocs.io/usage.html#installation) on both
machines and it seems to confirm that.

However, when I run my test program, Machine B reports FPS > 200, and
pygame.display.flip taking 0.002 per call,
while machine A reports FPS < 60, and pygame.display.flip taking 0.016 per
call.

This puzzles me: I was expecting a result similar to B on both machines,
since my loop does nothing - has no game logic, no physics, no image
handling. I was wondering if someone has any ideas to help me find out
what's wrong with
my setup in machine A. Btw, enabling fullscreen does not help, not does
changing the display resolution while
running in the windowed mode. Both displays are running at 60Hz.

MAchine A:
$ python fps_test.py
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
<VideoInfo(hw = 0, wm = 1,video_mem = 0
         blit_hw = 0, blit_hw_CC = 0, blit_hw_A = 0,
         blit_sw = 0, blit_sw_CC = 0, blit_sw_A = 0,
         bitsize  = 32, bytesize = 4,
         masks =  (16711680, 65280, 255, 0),
         shifts = (16, 8, 0, 0),
         losses =  (0, 0, 0, 8),
         current_w = 1440, current_h = 900
>

[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 54.64480972290039,
54.64480972290039, 54.64480972290039, 54.64480972290039, 54.64480972290039,
54.64480972290039, 54.64480972290039, 54.64480972290039, 54.64480972290039,
54.64480972290039, 57.47126770019531, 57.47126770019531, 57.47126770019531,
57.47126770019531, 57.47126770019531, 57.47126770019531, 57.47126770019531,
57.47126770019531, 57.47126770019531, 57.47126770019531, 58.82352828979492,
58.82352828979492, 58.82352828979492, 58.82352828979492, 58.82352828979492,
58.82352828979492, 58.82352828979492, 58.82352828979492, 58.82352828979492,
58.82352828979492, 59.52381134033203, 59.52381134033203, 59.52381134033203,
59.52381134033203, 59.52381134033203, 59.52381134033203, 59.52381134033203,
59.52381134033203, 59.52381134033203, 59.52381134033203, 59.88024139404297,
59.88024139404297, 59.88024139404297, 59.88024139404297, 59.88024139404297,
59.88024139404297, 59.88024139404297, 59.88024139404297, 59.88024139404297,
59.88024139404297, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 58.479530334472656, 58.479530334472656,
58.479530334472656, 59.17159652709961, 59.17159652709961,
59.17159652709961, 59.17159652709961, 59.17159652709961, 59.17159652709961,
59.17159652709961, 59.17159652709961, 59.17159652709961, 59.17159652709961]
         405 function calls in 1.760 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    1.760    1.760 <string>:1(<module>)
        1    0.002    0.002    1.760    1.760 fps_test.py:11(run)
        1    0.000    0.000    1.760    1.760 {built-in method
builtins.exec}
        1    0.000    0.000    0.000    0.000 {built-in method
builtins.print}
      100    1.644    0.016    1.644    0.016 {built-in method
pygame.display.flip}
        1    0.000    0.000    0.000    0.000 {method 'disable' of
'_lsprof.Profiler' objects}
      100    0.114    0.001    0.114    0.001 {method 'fill' of
'pygame.Surface' objects}
      100    0.000    0.000    0.000    0.000 {method 'get_fps' of 'Clock'
objects}
      100    0.000    0.000    0.000    0.000 {method 'tick' of 'Clock'
objects}



Machine B:

$ python fps_test.py
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
<VideoInfo(hw = 0, wm = 1,video_mem = 0
         blit_hw = 0, blit_hw_CC = 0, blit_hw_A = 0,
         blit_sw = 0, blit_sw_CC = 0, blit_sw_A = 0,
         bitsize  = 32, bytesize = 4,
         masks =  (16711680, 65280, 255, 0),
         shifts = (16, 8, 0, 0),
         losses =  (0, 0, 0, 8),
         current_w = 1440, current_h = 900
>

[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 256.4102478027344,
256.4102478027344, 256.4102478027344, 256.4102478027344, 256.4102478027344,
256.4102478027344, 256.4102478027344, 256.4102478027344, 256.4102478027344,
256.4102478027344, 263.15789794921875, 263.15789794921875,
263.15789794921875, 263.15789794921875, 263.15789794921875,
263.15789794921875, 263.15789794921875, 263.15789794921875,
263.15789794921875, 263.15789794921875, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781, 303.0303039550781,
303.0303039550781, 285.71429443359375, 285.71429443359375,
285.71429443359375, 285.71429443359375, 285.71429443359375,
285.71429443359375, 285.71429443359375, 285.71429443359375,
285.71429443359375, 285.71429443359375, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781,
285.71429443359375, 285.71429443359375, 285.71429443359375,
285.71429443359375, 285.71429443359375, 285.71429443359375,
285.71429443359375, 285.71429443359375, 285.71429443359375,
285.71429443359375, 303.0303039550781, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781, 303.0303039550781,
303.0303039550781, 303.0303039550781, 303.0303039550781, 303.0303039550781,
294.1176452636719, 294.1176452636719, 294.1176452636719, 294.1176452636719,
294.1176452636719, 294.1176452636719, 294.1176452636719, 294.1176452636719,
294.1176452636719, 294.1176452636719]
         405 function calls in 0.356 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.356    0.356 <string>:1(<module>)
        1    0.003    0.003    0.356    0.356 fps_test.py:11(run)
        1    0.000    0.000    0.356    0.356 {built-in method
builtins.exec}
        1    0.000    0.000    0.000    0.000 {built-in method
builtins.print}
      100    0.201    0.002    0.201    0.002 {built-in method
pygame.display.flip}
        1    0.000    0.000    0.000    0.000 {method 'disable' of
'_lsprof.Profiler' objects}
      100    0.150    0.002    0.150    0.002 {method 'fill' of
'pygame.Surface' objects}
      100    0.000    0.000    0.000    0.000 {method 'get_fps' of 'Clock'
objects}
      100    0.000    0.000    0.000    0.000 {method 'tick' of 'Clock'
objects}



Modified test program:
import pygame

pygame.init()
clock = pygame.time.Clock()
screen_flags = 0  # pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.FULLSCREEN
screen = pygame.display.set_mode((1440, 900), screen_flags)
pygame.display.set_caption("FPS Test")

print(pygame.display.Info())

def run():
    x = 0
    fps = [0] * 100
    while x < 100:
        screen.fill(pygame.Color(0, 0, 0))
        pygame.display.flip()
        clock.tick()
        fps[x] = clock.get_fps()
        x += 1
    print(fps)

import cProfile
cProfile.run('run()')



On Tue, Feb 18, 2020 at 6:29 PM Ian Mallett <i...@geometrian.com> wrote:

> Hi,
>
> First, there are some problems with this test code:
>
> - The first ten framerates will be 0.0 due to what I consider a misfeature
> in the way pygame reports framerate; this drags down your average. Even if
> you ran at a nominal 60Hz, the highest reported framerate is going to be
> 54fps (wherefore, I'm somewhat confused that you say you got 60Hz on a
> different machine; perhaps you were running a bit faster; e.g. I see 62Hz a
> lot on certain machines).
>
> - Averaging the framerates is a bit problematic because that is a
> reciprocal measure. It is better to average the frame latencies and then
> reciprocate that to get the average framerate.
>
> - You're attempting to synchronize the framerate to 60Hz, this can cause
> aliasing issues since pygame is software-backed, not graphics backed,
> exactly. E.g., if your code is actually 59fps, then the fastest it can
> update will be 30fps. It would be better to not include the argument to
> `.tick(...)` at all.
>
> Which brings us to the main reason:
>
> - Setting millions of pixels is not a job for the CPU; it's a job for the
> graphics card. However, pygame runs on the CPU. As you observe, larger
> displays run slower. If you want good refresh-bound performance, you
> probably want to make a GL context and draw with that, without disabling
> VSync. This is, in fact, what glxgears does, which is why it's running at a
> reasonable framerate.
>
> Ian
>

Reply via email to