Python-Klassen sind langsam

Im Sinne von Java und C ++ scheinen Klassenmitgliedsreferenzen schnell und Kartenreferenzen langsam zu sein, aber dies ist bei Python nicht immer der Fall. Dies liegt daran, dass Python die Klasse intern als eine Art Dikt behandelt. Selbst wenn Sie Dikt im normalen Gebrauch verwenden, ist dies in Bezug auf die Geschwindigkeit nicht so nachteilig [^ 1].

[^ 1]: Es wurde darauf hingewiesen, dass die Speicherzuordnung und die Referenzzeit getrennt gemessen werden müssen, sodass einige Messelemente korrigiert wurden. Ein Teil des Inhalts wurde entsprechend geändert.

Validierungsskript


import collections
import random
import time


#
#In der Klasse implementiert.
#
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)


#
#In der Klasse implementiert (mit 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)


#
#Implementiert mit dikt.
#
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)


#
#Implementiert mit 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)


#
#Implementiert mit Tupel.
#
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)


#
#Implementiert mit 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)


#
#Führen Sie einen Test durch.
#
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))

Die Ergebnisse sind unten gezeigt, und wenn man sich auf die Zeit konzentriert, die für die Referenzoperation erforderlich ist, kann man sehen, dass sich die Implementierung unter Verwendung von dict nicht wesentlich von der Implementierung unter Verwendung der normalen Klasse unterscheidet. Wenn Sie einen Steckplatz verwenden, können Sie etwas schneller als dikt darauf zugreifen. Das Erstellen eines Objekts dauert jedoch fast doppelt so lange wie diktieren und auflisten. Wenn Sie also die Zeit berücksichtigen, die zum Sichern von Elementen erforderlich ist, liegt dieser Unterschied darin. Es wird eine fast bedeutungslose Zahl.

Ausführungsergebnis


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

Obwohl die Liste für Referenzoperationen am schnellsten ist, gibt es keinen Grund, sie für die oben genannten Zwecke zu verwenden, wenn man bedenkt, wie viel Zeit und Aufwand erforderlich ist, um ein Element als Index anzugeben. Tatsächlich stellte sich heraus, dass das Tupel, das ich heimlich erwartet hatte, in Bezug auf die Referenzgeschwindigkeit nicht so schnell war. Es ist jedoch am schnellsten, die Elemente zu sichern, und da die Werte nach der Erstellung nicht geändert werden können, kann gesagt werden, dass dies für den Austausch kleiner Werte ausreicht.

namedtuple ist sehr langsam zu generieren und zu referenzieren. Dies liegt daran, dass namedtuple jedes Mal von Anfang an nach Mitgliedern sucht. Obwohl die Speichereffizienz gut ist, wird die Geschwindigkeit langsamer, wenn die Anzahl der Elemente zunimmt.

Recommended Posts

Python-Klassen sind langsam
Wie Python-Klassen und magische Methoden funktionieren.
Python-Grundkurs (13 Klassen)
Informationen zu Python-Objekten und -Klassen
Python-Kurs zum Lernen mit Chemoinfomatik
Paiza Python Primer 8: Grundlegendes zu Klassen
Python a + = b und a = a + b sind unterschiedlich
Python> diese werden alle als falsch betrachtet:
Dieser Python-Code hat keine Klassen ...
ABC für Python-Abstract-Klassen und Ententypisierung
Python
Python: Ein Hinweis zu Klasse 1 "Abstract"
Was war überraschend an Python-Klassen?
[Python] Was sind @classmethod und Dekorateure?
[Python] Über Executor und zukünftige Klassen
Python-Klassen und -Instanzen, Instanzmethoden
Ich höre, dass Matlab- und Python-Schleifen langsam sind, aber ist das wahr?
Module und Pakete in Python sind "Namespaces"
Alle Python-Argumente werden als Referenz übergeben
Binden Sie Methoden an Python-Klassen und -Instanzen
Ein Forscher eines Pharmaunternehmens fasste die Klassen in Python zusammen
10 Python-Fehler, die Anfängern häufig sind
Ich schaute wieder auf die Python-Klasse zurück
Apropos alte und neue Klassen in Python
Python open und io.open sind gleich