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