2
\$\begingroup\$

I get 25% CPU usage on this simple project. I hope it's okay if I just post the whole thing. It probably something to do with the rendering code. It's definitely the project though, my fans get loud, then I exit the program, and quiet again.

#python3 -OO main.py
import pygame
import json
import sys
import os
#OS
xMax = 800
yMax = 640
xWin = 1366 - xMax #pins window to top right of the display
yWin = 0
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" % (xWin, yWin)
#PYGAME
pygame.init()
pygame.font.init()
pygame.display.set_caption("Mountain of Doom")
screen = pygame.display.set_mode((xMax, yMax))
font = pygame.font.SysFont(None, 30)
with open('saves/save.json') as save_file:
 data = json.load(save_file)
 
tileSize = 16
tile_images = {
 1: pygame.image.load("resources/graphics/sprites/grass.png"),
 #ABOUT 10 MORE CALLS
}
with open("resources/maps/demo.json", "r") as json_file:
 json_data = json.load(json_file)
 tileMap = json_data["tileMap"]
class Background:
 def __init__(self, tile_images, tileSize, tileMap):
 self.tile_images = tile_images
 self.tileSize = tileSize
 self.tileMap = tileMap
 def draw(self, screen):
 background = pygame.Surface((len(self.tileMap[0]) * self.tileSize, len(self.tileMap) * self.tileSize))
 for row in range(len(self.tileMap)):
 for col in range(len(self.tileMap[0])):
 tile_id = self.tileMap[row][col]
 if tile_id in self.tile_images:
 tile_image = self.tile_images[tile_id]
 background.blit(tile_image, (col * self.tileSize, row * self.tileSize))
 screen.blit(background, (0, 0))
class Player:
 def __init__(self, position, player_path):
 self.position = position
 self.image = pygame.image.load(player_path)
 self.speed = 16
 
 def move(self, keys):
 x, y = self.position
 speed = self.speed
 
 if keys[pygame.K_UP] and y > 0:
 y -= speed
 elif keys[pygame.K_DOWN]:
 y += speed
 elif keys[pygame.K_LEFT] and x > 0:
 x -= speed
 elif keys[pygame.K_RIGHT]:
 x += speed
 self.position = (x, y)
 
 def draw(self, screen):
 screen.blit(self.image, (self.position))
 
def update_data(player):
 data['player_x'] = player.position[0]
 data['player_y'] = player.position[1]
 with open('saves/save.json', 'w') as save_file:
 json.dump(data, save_file)
 
def main():
 background = Background(tile_images, tileSize, tileMap)
 player_path = "resources/graphics/sprites/player1.png"
 position = (data['player_x'], data['player_y'])
 player = Player(position, player_path)
 clock = pygame.time.Clock()
 while True:
 clock.tick(60)
 mbreak = False
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
 mbreak = True
 keys = pygame.key.get_pressed()
 if keys[pygame.K_ESCAPE]:
 mbreak = True
 background.draw(screen)
 player.move(keys)
 player.draw(screen)
 text = f'{player.position[0]}, {player.position[1]}'
 img = font.render(text, True, (0xFFFFFFFF))
 screen.blit(img, (20, 20))
 
 pygame.display.update()
 if mbreak:
 break
 
 update_data(player)
 pygame.quit()
 sys.exit()
if __name__ == "__main__":
 main()
asked Aug 19, 2023 at 1:47
\$\endgroup\$
10
  • 3
    \$\begingroup\$ Welcome to Code Review! To help reviewers give you better answers, we need to know what the code is intended to achieve. Please add sufficient context to your question to describe the purpose of the code. We want to know why much more than how. The more you tell us about what your code is for, the easier it will be for reviewers to help you. Also, edit the title to simply summarise the task, rather than your concerns about the code. \$\endgroup\$ Commented Aug 19, 2023 at 8:46
  • \$\begingroup\$ What hardware? What fraction of a CPU core does it occupy when running, according to your OS's process monitoring tools, e.g. top on Linux? IDK if it's worth profiling it with perf record to see if there's a lot of CPU time spent in a shared library (such as a graphics lib). Also, if your X server uses a lot of CPU time while this is running, that could also be a thing. Do you have proper graphics drivers for your video hardware set up? \$\endgroup\$ Commented Aug 19, 2023 at 10:27
  • \$\begingroup\$ Did you write this game? Posting the whole game is fine. \$\endgroup\$ Commented Aug 19, 2023 at 13:11
  • 3
    \$\begingroup\$ External links are not up for review. Links can rot. \$\endgroup\$ Commented Aug 19, 2023 at 14:12
  • 1
    \$\begingroup\$ i5-4200M has an integrated GPU, Intel® HD Graphics 4600. It's not very powerful, but it supports OpenGL and stuff so it's much more than a dumb framebuffer. Even on Linux the drivers should be set up automatically since they're open source. When you say 25% CPU usage, are you on Windows which reports CPU load as a fraction of the total logical cores? So "25%" of your 2c4t CPU is all of one logical core, i.e. your program running as fast as the CPU + GPU is able to run it, not sleeping to wait for input or for time until the next frame. \$\endgroup\$ Commented Aug 19, 2023 at 18:34

2 Answers 2

11
\$\begingroup\$

The code is not easy to run because I don't have your local files. However, I was able to throw some together in MS Paint. Although I didn't notice any performance issues (you might need to update your packages?), I think that the main issue is that you are repeatedly computing the background Surface in your Background class. Personally, I would just put together the background once and then blit it for each drawing animation. You also might want some input validation.

class Background:
 def __init__(self, tile_images, tile_size, tile_map):
 """
 Creates a background image and makes it easy to draw.
 :param tile_images: from tile_id to source
 :param tile_size: positive integer number of pixels
 :param tile_map: 2D array from (row, col) to tile_id
 """
 if tile_size < 1:
 raise ValueError("Tile size less than 1 pixel!")
 n_rows = len(tile_map)
 if n_rows < 1:
 raise ValueError("Less than 1 map row!")
 n_cols = len(tile_map[0])
 if n_cols < 1:
 raise ValueError("Less than 1 map col!")
 background = pygame.Surface((n_cols * tile_size, n_rows * tile_size))
 for row in range(n_rows):
 for col in range(n_cols):
 tile_id = tile_map[row][col]
 tile_image = tile_images[tile_id]
 background.blit(tile_image, (col * tile_size, row * tile_size))
 self.background = background
 def draw(self, screen):
 screen.blit(self.background, (0, 0))

That change reduced the CPU utilization from an average of 2.5% to an average of 0.5% for me. Hope that helps. Welcome to the community.

answered Aug 19, 2023 at 3:07
\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is what jumped out at me as well. Creating objects in a tight loop in pygame (and they're all tight loops) is a red flag. Ideally do all your object creation at init-time. \$\endgroup\$ Commented Aug 20, 2023 at 2:23
6
\$\begingroup\$

FROM:

tile_images = {
 1: pygame.image.load("resources/graphics/sprites/grass.png"),
 2: pygame.image.load("resources/graphics/sprites/water.png"),
 3: pygame.image.load("resources/graphics/sprites/tree.png").,
 4: pygame.image.load("resources/graphics/sprites/bones.png"),
 5: pygame.image.load("resources/graphics/sprites/rocks.png"),
 6: pygame.image.load("resources/graphics/sprites/brick1.png"),
 7: pygame.image.load("resources/graphics/sprites/brick2.png"),
 8: pygame.image.load("resources/graphics/sprites/tree2.png")
} 

TO:

 tile_images = {
 1: pygame.image.load("resources/graphics/sprites/grass.png").convert_alpha(),
 2: pygame.image.load("resources/graphics/sprites/water.png").convert_alpha(),
 3: pygame.image.load("resources/graphics/sprites/tree.png").convert_alpha(),
 4: pygame.image.load("resources/graphics/sprites/bones.png").convert_alpha(),
 5: pygame.image.load("resources/graphics/sprites/rocks.png").convert_alpha(),
 6: pygame.image.load("resources/graphics/sprites/brick1.png").convert_alpha(),
 7: pygame.image.load("resources/graphics/sprites/brick2.png").convert_alpha(),
 8: pygame.image.load("resources/graphics/sprites/tree2.png").convert_alpha()
 }

This got my CPU usage down from 25%-26% to 7%-8%.

answered Aug 19, 2023 at 14:48
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.