Python импортирует глобальные переменные быстрее, чем локальные переменные?

Итак, вместе с друзьями я делаю игру на Pygame, и я только что начал программировать анимацию для главного героя.

Я подумал, что есть несколько способов сделать это. Либо составьте список изображений и просто перебирайте список, либо создайте словарь, содержащий координаты изображений на листе спрайтов.

Я выбрал второй вариант и решил проверить, какой из них быстрее. Поэтому я запрограммировал их обоих довольно упрощенным способом, просто чтобы быстро проверить, как они работают.

Результаты были очень близки друг к другу (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))

и это код (разделенный на 2 файла), который выполняется примерно за 0,021 секунды: Файл 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))

Обновлено: как было предложено матчем, я изменил метод измерения времени и вместо этого использовал профилирование python (с использованием cProfile), но я получил примерно те же результаты, за исключением того, что время для первых двух методов оказалось немного дольше

Такое измерение времени - не очень точный способ измерения производительности, особенно для быстрых операций. Лучше всего использовать что-то вроде timeit для конкретной части, которая, по вашему мнению, может повлиять на производительность, или использовать профилировщик.

match 11.03.2018 00:03

@match Вместо этого я перешел на профилирование python и отредактировал сообщение, но мой результат не сильно изменился

Cheryl Lamb 11.03.2018 00:38
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

С отдельным файлом все вызовы getImage происходят при первом импорте файла. Вы не рассчитываете время импорта, поэтому игнорируете стоимость всего, что делает файл.

Я поместил предложение синхронизации над импортом, и сейчас он также синхронизируется, но метод импорта по-прежнему намного быстрее, поскольку программа выполняется только на 0,02 секунды медленнее и заканчивается на 0,041 секунды по сравнению с другими, которые имеют 0,41 и 0,40

Cheryl Lamb 11.03.2018 00:49
Ответ принят как подходящий

Я считаю, что разница здесь связана с компиляцией байт-кода python для любых импортированных модулей. Это, в свою очередь, ускорит загрузку / выполнение импорта. Вы можете увидеть их как файлы .pyc в каталоге вместе с исходным кодом.

Я подозреваю, что если вы сделаете следующее, ваши результаты снова выровняются:

rm *.pyc
PYTHONDONTWRITEBYTECODE=1 python mygame.py

Также имейте в виду, что вероятность 0,02 секунды находится в пределах обычного «дрейфа» любой системы - вот почему такие инструменты, как timeit, запускают один и тот же код много тысяч раз и усредняют результаты.

В этой заметке - подумайте, сколько раз ваша потенциально медленная операция происходит в коде - если вы загружаете изображения только один раз, а прирост производительности, выполняя это иначе, составляет 0,02 секунды - в то время как игра, как ожидается, будет работать в течение минут или часов, тогда это подозрительно похоже на преждевременную оптимизацию.

Дело в том, что изменение составляет не 0,02 секунды, а 0,2 секунды, что, я бы сказал, большая разница (хотя я могу ошибаться).

Cheryl Lamb 11.03.2018 12:16

Другие вопросы по теме