Python 导入全局变量比局部变量更快?
Python importing globals faster than local variables?
所以我和我的朋友一起在 Pygame 制作游戏,我刚刚开始为主角编写动画。
我想有几种方法可以做到这一点。要么制作一个图像列表并在列表上循环,要么制作一个包含精灵上图片坐标的字典 sheet。
我选择了第二个选项,现在我决定要检查哪个更快。所以我以一种非常简单的方式对它们进行了编程,只是为了快速检查它们的性能。
结果彼此非常接近(使用词典时为 0.41,不使用时约为 0.40)。然后我决定尝试如果我使用第一个选项会发生什么,除了我会在一个单独的文件中调用 pygame.image.load
并将动画帧存储为全局变量,然后导入到主文件中。
我虽然会很慢,因为我在某处读到 python 导入非常慢......但令人惊讶的是我得到了 0.021 秒的结果!
这是对性能的巨大改变,这可能对我的游戏至关重要,所以我想知道是否有人碰巧知道为什么这种方法快得多,是否只是因为某些 x 或 y 为什么它在这种情况下更快,而在另一种情况下会非常慢。
这是类似于我游戏当前状态的代码:
import pygame
import time
start_time = time.time()
playerSpriteSize = 192
img = (0, 0)
class SpriteSheet():
def __init__(self, filename):
self.sheet = pygame.image.load(filename).convert()
def get_image(self, coords, size, flip=False):
surf = pygame.Surface(size).convert()
surf.blit(self.sheet, (0, 0), (coords[0], coords[1], size[0], size[1]))
pygame.transform.flip(surf, False, True)
surf.set_colorkey((0, 0, 0))
surf = pygame.transform.flip(surf, flip, False)
return surf
pygame.init()
displaySurface = pygame.display.set_mode((400, 400))
x = SpriteSheet("playerAnimation.png")
animationList = {0: (0, playerSpriteSize*0), 1: (0, playerSpriteSize*1), 2: (0, playerSpriteSize*2),
3: (0, playerSpriteSize*3), 4: (0, playerSpriteSize*4), 5: (0, playerSpriteSize*5),
6: (0, playerSpriteSize*6)}
rounda = 0
for i in animationList:
a1 = x.get_image(animationList[rounda], (playerSpriteSize, playerSpriteSize))
displaySurface.blit(a1, (30, 30))
rounda += 1
pygame.display.update()
print("--- %s seconds ---" %(time.time() - start_time))
现在这是使用在主游戏循环之前加载的图像的代码:
import pygame
import time
start_time = time.time()
imageSize = 192
img = (0, 0)
class SpriteSheet():
def __init__(self, filename):
self.sheet = pygame.image.load(filename).convert()
def get_image(self, coords, size, flip=False):
surf = pygame.Surface(size).convert()
surf.blit(self.sheet, (0, 0), (coords[0], coords[1], size[0], size[1]))
pygame.transform.flip(surf, False, True)
surf.set_colorkey((0, 0, 0))
surf = pygame.transform.flip(surf, flip, False)
return surf
pygame.init()
displaySurface = pygame.display.set_mode((400, 400))
x = SpriteSheet("playerAnimation.png")
a1 = x.get_image((0, 0), (imageSize, imageSize))
a2 = x.get_image((0, 192), (imageSize, imageSize))
a3 = x.get_image((0, 384), (imageSize, imageSize))
a4 = x.get_image((0, 576), (imageSize, imageSize))
a5 = x.get_image((0, 768), (imageSize, imageSize))
a6 = x.get_image((0, 960), (imageSize, imageSize))
a7 = x.get_image((0, 1152), (imageSize, imageSize))
animationList = [a1, a2, a3, a4, a5, a6, a7]
for i in animationList:
displaySurface.blit(i, (30, 30))
pygame.display.update()
print("--- %s seconds ---" %(time.time() - start_time))
这是在大约 0.021 秒内运行的代码(分为 2 个文件):
文件 1(主文件)
import pygame
import time
from mainDifferentExtern import a1, a2, a3, a4, a5, a6, a7
start_time = time.time()
imageSize = 192
img = (0, 0)
class SpriteSheet():
def __init__(self, filename):
self.sheet = pygame.image.load(filename).convert()
def get_image(self, coords, size, flip=False):
surf = pygame.Surface(size).convert()
surf.blit(self.sheet, (0, 0), (coords[0], coords[1], size[0], size[1]))
pygame.transform.flip(surf, False, True)
surf.set_colorkey((0, 0, 0))
surf = pygame.transform.flip(surf, flip, False)
return surf
pygame.init()
displaySurface = pygame.display.set_mode((400, 400))
x = SpriteSheet("playerAnimation.png")
animationList = [a1, a2, a3, a4, a5, a6, a7]
for i in animationList:
displaySurface.blit(i, (30, 30))
pygame.display.update()
print("--- %s seconds ---" %(time.time() - start_time))
及其从中导入的文件:
import pygame
imageSize = 192
class SpriteSheet():
def __init__(self, filename):
self.sheet = pygame.image.load(filename).convert()
def get_image(self, coords, size, flip=False):
surf = pygame.Surface(size).convert()
surf.blit(self.sheet, (0, 0), (coords[0], coords[1], size[0], size[1]))
pygame.transform.flip(surf, False, True)
surf.set_colorkey((0, 0, 0))
surf = pygame.transform.flip(surf, flip, False)
return surf
pygame.init()
pygame.display.set_mode((1, 1))
x = SpriteSheet("playerAnimation.png")
a1 = x.get_image((0, 0), (imageSize, imageSize))
a2 = x.get_image((0, 192), (imageSize, imageSize))
a3 = x.get_image((0, 384), (imageSize, imageSize))
a4 = x.get_image((0, 576), (imageSize, imageSize))
a5 = x.get_image((0, 768), (imageSize, imageSize))
a6 = x.get_image((0, 960), (imageSize, imageSize))
a7 = x.get_image((0, 1152), (imageSize, imageSize))
编辑:根据 match 的建议,我改变了测量时间的方法,而是使用 python 分析(使用 cProfile),但我得到的结果大致相同,除了时间前两种方法出来的时间有点长
对于单独的文件,所有 getImage
调用都在首次导入文件时发生。您没有为导入计时,因此您忽略了该文件所做的一切的成本。
我认为这里的差异是由于 python 为任何导入的模块编译字节码。这反过来将加快导入的 loading/execution。您可以在原始代码旁边的目录中看到这些 .pyc
文件。
我怀疑如果您执行以下操作,您的结果将再次平衡:
rm *.pyc
PYTHONDONTWRITEBYTECODE=1 python mygame.py
另请记住,0.02 秒的机会完全在任何系统的正常 'drift' 范围内 - 这就是为什么像 timeit
运行 这样的工具可以使用数千个相同的代码次并对结果进行平均。
关于这一点 - 考虑一下您在代码中可能出现的缓慢操作有多少次 - 如果您只加载一次图像,并且通过不同的方式执行的性能增益为 0.02 秒 - 而游戏预计 运行 几分钟或几小时,那么这看起来像是过早的优化。
所以我和我的朋友一起在 Pygame 制作游戏,我刚刚开始为主角编写动画。
我想有几种方法可以做到这一点。要么制作一个图像列表并在列表上循环,要么制作一个包含精灵上图片坐标的字典 sheet。
我选择了第二个选项,现在我决定要检查哪个更快。所以我以一种非常简单的方式对它们进行了编程,只是为了快速检查它们的性能。
结果彼此非常接近(使用词典时为 0.41,不使用时约为 0.40)。然后我决定尝试如果我使用第一个选项会发生什么,除了我会在一个单独的文件中调用 pygame.image.load
并将动画帧存储为全局变量,然后导入到主文件中。
我虽然会很慢,因为我在某处读到 python 导入非常慢......但令人惊讶的是我得到了 0.021 秒的结果!
这是对性能的巨大改变,这可能对我的游戏至关重要,所以我想知道是否有人碰巧知道为什么这种方法快得多,是否只是因为某些 x 或 y 为什么它在这种情况下更快,而在另一种情况下会非常慢。
这是类似于我游戏当前状态的代码:
import pygame
import time
start_time = time.time()
playerSpriteSize = 192
img = (0, 0)
class SpriteSheet():
def __init__(self, filename):
self.sheet = pygame.image.load(filename).convert()
def get_image(self, coords, size, flip=False):
surf = pygame.Surface(size).convert()
surf.blit(self.sheet, (0, 0), (coords[0], coords[1], size[0], size[1]))
pygame.transform.flip(surf, False, True)
surf.set_colorkey((0, 0, 0))
surf = pygame.transform.flip(surf, flip, False)
return surf
pygame.init()
displaySurface = pygame.display.set_mode((400, 400))
x = SpriteSheet("playerAnimation.png")
animationList = {0: (0, playerSpriteSize*0), 1: (0, playerSpriteSize*1), 2: (0, playerSpriteSize*2),
3: (0, playerSpriteSize*3), 4: (0, playerSpriteSize*4), 5: (0, playerSpriteSize*5),
6: (0, playerSpriteSize*6)}
rounda = 0
for i in animationList:
a1 = x.get_image(animationList[rounda], (playerSpriteSize, playerSpriteSize))
displaySurface.blit(a1, (30, 30))
rounda += 1
pygame.display.update()
print("--- %s seconds ---" %(time.time() - start_time))
现在这是使用在主游戏循环之前加载的图像的代码:
import pygame
import time
start_time = time.time()
imageSize = 192
img = (0, 0)
class SpriteSheet():
def __init__(self, filename):
self.sheet = pygame.image.load(filename).convert()
def get_image(self, coords, size, flip=False):
surf = pygame.Surface(size).convert()
surf.blit(self.sheet, (0, 0), (coords[0], coords[1], size[0], size[1]))
pygame.transform.flip(surf, False, True)
surf.set_colorkey((0, 0, 0))
surf = pygame.transform.flip(surf, flip, False)
return surf
pygame.init()
displaySurface = pygame.display.set_mode((400, 400))
x = SpriteSheet("playerAnimation.png")
a1 = x.get_image((0, 0), (imageSize, imageSize))
a2 = x.get_image((0, 192), (imageSize, imageSize))
a3 = x.get_image((0, 384), (imageSize, imageSize))
a4 = x.get_image((0, 576), (imageSize, imageSize))
a5 = x.get_image((0, 768), (imageSize, imageSize))
a6 = x.get_image((0, 960), (imageSize, imageSize))
a7 = x.get_image((0, 1152), (imageSize, imageSize))
animationList = [a1, a2, a3, a4, a5, a6, a7]
for i in animationList:
displaySurface.blit(i, (30, 30))
pygame.display.update()
print("--- %s seconds ---" %(time.time() - start_time))
这是在大约 0.021 秒内运行的代码(分为 2 个文件): 文件 1(主文件)
import pygame
import time
from mainDifferentExtern import a1, a2, a3, a4, a5, a6, a7
start_time = time.time()
imageSize = 192
img = (0, 0)
class SpriteSheet():
def __init__(self, filename):
self.sheet = pygame.image.load(filename).convert()
def get_image(self, coords, size, flip=False):
surf = pygame.Surface(size).convert()
surf.blit(self.sheet, (0, 0), (coords[0], coords[1], size[0], size[1]))
pygame.transform.flip(surf, False, True)
surf.set_colorkey((0, 0, 0))
surf = pygame.transform.flip(surf, flip, False)
return surf
pygame.init()
displaySurface = pygame.display.set_mode((400, 400))
x = SpriteSheet("playerAnimation.png")
animationList = [a1, a2, a3, a4, a5, a6, a7]
for i in animationList:
displaySurface.blit(i, (30, 30))
pygame.display.update()
print("--- %s seconds ---" %(time.time() - start_time))
及其从中导入的文件:
import pygame
imageSize = 192
class SpriteSheet():
def __init__(self, filename):
self.sheet = pygame.image.load(filename).convert()
def get_image(self, coords, size, flip=False):
surf = pygame.Surface(size).convert()
surf.blit(self.sheet, (0, 0), (coords[0], coords[1], size[0], size[1]))
pygame.transform.flip(surf, False, True)
surf.set_colorkey((0, 0, 0))
surf = pygame.transform.flip(surf, flip, False)
return surf
pygame.init()
pygame.display.set_mode((1, 1))
x = SpriteSheet("playerAnimation.png")
a1 = x.get_image((0, 0), (imageSize, imageSize))
a2 = x.get_image((0, 192), (imageSize, imageSize))
a3 = x.get_image((0, 384), (imageSize, imageSize))
a4 = x.get_image((0, 576), (imageSize, imageSize))
a5 = x.get_image((0, 768), (imageSize, imageSize))
a6 = x.get_image((0, 960), (imageSize, imageSize))
a7 = x.get_image((0, 1152), (imageSize, imageSize))
编辑:根据 match 的建议,我改变了测量时间的方法,而是使用 python 分析(使用 cProfile),但我得到的结果大致相同,除了时间前两种方法出来的时间有点长
对于单独的文件,所有 getImage
调用都在首次导入文件时发生。您没有为导入计时,因此您忽略了该文件所做的一切的成本。
我认为这里的差异是由于 python 为任何导入的模块编译字节码。这反过来将加快导入的 loading/execution。您可以在原始代码旁边的目录中看到这些 .pyc
文件。
我怀疑如果您执行以下操作,您的结果将再次平衡:
rm *.pyc
PYTHONDONTWRITEBYTECODE=1 python mygame.py
另请记住,0.02 秒的机会完全在任何系统的正常 'drift' 范围内 - 这就是为什么像 timeit
运行 这样的工具可以使用数千个相同的代码次并对结果进行平均。
关于这一点 - 考虑一下您在代码中可能出现的缓慢操作有多少次 - 如果您只加载一次图像,并且通过不同的方式执行的性能增益为 0.02 秒 - 而游戏预计 运行 几分钟或几小时,那么这看起来像是过早的优化。