Les classes Python sont lentes

Au sens de Java et C ++, il semble que les références de membres de classe soient rapides et les références de mappe lentes, mais ce n'est pas toujours le cas avec Python. C'est parce que Python traite la classe comme une sorte de dict en interne, donc même si vous utilisez simplement dict dans un usage normal, ce ne sera pas si désavantageux en termes de vitesse [^ 1].

[^ 1]: Il a été souligné qu'il était nécessaire de mesurer séparément l'allocation de mémoire et le temps de référence, de sorte que certains éléments de mesure ont été corrigés. Une partie du contenu a été modifiée en conséquence.

Script de validation


import collections
import random
import time


#
#Implémenté en classe.
#
class EClass:
	
	def __init__(self, x, y):
		self.x = x
		self.y = y

def test_class(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = EClass(x, y)
		l.append(e)
	t2 = time.time()
	for e in l:
		x = e.x
		y = e.y
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Implémenté en classe (avec slots).
#
class EClassSlot:
	
	__slots__ = ["x", "y"]
	
	def __init__(self, x, y):
		self.x = x
		self.y = y

def test_class_slot(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = EClassSlot(x, y)
		l.append(e)
	t2 = time.time()
	for e in l:
		x = e.x
		y = e.y
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Mis en œuvre avec dict.
#
def test_dict(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = {"x": x, "y": y}
		l.append(e)
	t2 = time.time()
	for e in l:
		x = e["x"]
		y = e["y"]
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Mis en œuvre avec liste.
#
def test_list(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = [x, y]
		l.append(e)
	t2 = time.time()
	for e in l:
		x = e[0]
		y = e[1]
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Implémenté avec tuple.
#
def test_tuple(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = ((x, y, None))
		l.append(e)
	t2 = time.time()
	for i, e in enumerate(l):
		x = e[0]
		y = e[1]
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Implémenté avec namedtuple.
#
ENamedTuple = collections.namedtuple("E", ("x", "y"))

def test_namedtuple(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = ENamedTuple(x, y)
		l.append(e)
	t2 = time.time()
	for i, e in enumerate(l):
		x = e.x
		y = e.y
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Faites un test.
#
tests = [
	("class\t", test_class), 
	("class_slot", test_class_slot), 
	("dict\t", test_dict), 
	("list\t", test_list), 
	("tuple\t", test_tuple), 
	("namedtuple", test_namedtuple)
]

n = 5000000
print("test\t\talloc\trefer\ttotal")

for test, function in tests:
	t1, t2, t3 = function(n)
	print("%s\t%0.2f\t%0.2f\t%0.2f" 
		%(test, t2 - t1, t3 - t2, t3 - t1))

Les résultats sont présentés ci-dessous, et en se concentrant sur le temps requis pour l'opération de référence, on peut voir que l'implémentation utilisant dict n'est pas très différente de l'implémentation utilisant la classe normale. Si vous utilisez slot, vous pouvez y accéder un peu plus rapidement que dict, mais la création d'un objet prend presque deux fois plus de temps que dict et list, donc si vous envisagez d'inclure le temps nécessaire pour sécuriser les éléments, cette différence est à ce sujet. Cela devient un nombre presque dénué de sens.

Résultat d'exécution


test		alloc	refer	total
class		7.68	0.84	8.52
class_slot	6.41	0.76	7.17
dict		3.64	0.79	4.43
list		4.00	0.72	4.72
tuple		2.41	0.96	3.37
namedtuple	7.50	1.70	9.20

Bien que la liste soit la plus rapide pour les opérations de référence, il n'y a aucune raison de l'utiliser aux fins ci-dessus, compte tenu du temps et des efforts nécessaires pour spécifier un élément comme index. En fait, il s'est avéré que le tuple auquel je m'attendais secrètement n'était pas si rapide en termes de vitesse de référence. Cependant, c'est le plus rapide pour sécuriser les éléments, et étant donné que les valeurs ne peuvent pas être modifiées après la création, on peut dire que cela suffit pour échanger de petites valeurs.

namedtuple est très lent à générer et à référencer. En effet, namedtuple recherche les membres dans l'ordre depuis le début à chaque fois, et bien que l'efficacité de la mémoire soit bonne, la vitesse devient plus lente à mesure que le nombre d'éléments augmente.

Recommended Posts

Les classes Python sont lentes
Comment fonctionnent les classes python et les méthodes magiques.
Cours de base Python (13 cours)
À propos des objets et des classes Python
Classe Python pour apprendre avec la chimioinfomatique
Paiza Python Primer 8: Comprendre les classes
Python a + = b et a = a + b sont différents
Python> ceux-ci sont tous considérés comme faux:
Ce code Python n'a pas de classes ...
ABC pour les classes abstraites Python et la saisie de canard
Python
Python: une note sur les classes 1 "Résumé"
Ce qui était surprenant dans les classes Python
[Python] Que sont @classmethod et les décorateurs?
[Python] À propos des classes Executor et Future
Classes et instances Python, méthodes d'instance
J'entends que les boucles Matlab et Python sont lentes, mais est-ce vrai?
Les modules et packages en Python sont des "espaces de noms"
Tous les arguments Python sont passés par référence
Lier des méthodes aux classes et instances Python
Des chercheurs de sociétés pharmaceutiques ont résumé les classes en Python
10 erreurs Python communes aux débutants
J'ai repensé à la classe Python
Parler d'anciennes et de nouvelles classes en Python
Python open et io.open sont les mêmes