Wie bereits von anderen erklärt, es ist nicht nur die Referenzen zu kopieren , sondern erhöht auch die Referenzzahl innerhalb der Objekte und damit die Objekte werden abgerufen und der Cache eine Rolle spielt.
Hier möchte ich nur weitere Experimente hinzufügen. Nicht so sehr über gemischt oder nicht gemischt (wo beim Zugriff auf ein Element der Cache möglicherweise übersehen wird, aber die folgenden Elemente in den Cache gelangen, damit sie getroffen werden). Aber über das Wiederholen von Elementen, bei denen spätere Zugriffe auf dasselbe Element den Cache treffen könnten, weil sich das Element noch im Cache befindet.
Testen eines normalen Bereichs:
>>> from timeit import timeit
>>> a = range(10**7)
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[5.1915339142808925, 5.1436351868889645, 5.18055115701749]
Eine Liste derselben Größe, bei der jedoch nur ein Element immer wieder wiederholt wird, ist schneller, da sie ständig in den Cache gelangt:
>>> a = [0] * 10**7
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[4.125743135926939, 4.128927210087596, 4.0941229388550795]
Und es scheint egal zu sein, um welche Zahl es sich handelt:
>>> a = [1234567] * 10**7
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[4.124106479141709, 4.156590225249886, 4.219242600790949]
Interessanterweise wird es noch schneller, wenn ich stattdessen dieselben zwei oder vier Elemente wiederhole:
>>> a = [0, 1] * (10**7 / 2)
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[3.130586101607932, 3.1001001764957294, 3.1318465707127814]
>>> a = [0, 1, 2, 3] * (10**7 / 4)
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[3.096105435911994, 3.127148431279352, 3.132872673690855]
Ich denke, etwas mag es nicht, wenn derselbe einzelne Zähler ständig erhöht wird. Vielleicht ist eine Pipeline stehen geblieben, weil jede Erhöhung auf das Ergebnis der vorherigen Erhöhung warten muss, aber dies ist eine wilde Vermutung.
Wie auch immer, versuchen Sie dies für eine noch größere Anzahl von wiederholten Elementen:
from timeit import timeit
for e in range(26):
n = 2**e
a = range(n) * (2**25 / n)
times = [timeit(lambda: list(a), number=20) for _ in range(3)]
print '%8d ' % n, ' '.join('%.3f' % t for t in times), ' => ', sum(times) / 3
Die Ausgabe (erste Spalte ist die Anzahl der verschiedenen Elemente, für die ich dreimal teste und dann den Durchschnitt nehme):
1 2.871 2.828 2.835 => 2.84446732686
2 2.144 2.097 2.157 => 2.13275338734
4 2.129 2.297 2.247 => 2.22436720645
8 2.151 2.174 2.170 => 2.16477771575
16 2.164 2.159 2.167 => 2.16328197911
32 2.102 2.117 2.154 => 2.12437970598
64 2.145 2.133 2.126 => 2.13462250728
128 2.135 2.122 2.137 => 2.13145065221
256 2.136 2.124 2.140 => 2.13336283943
512 2.140 2.188 2.179 => 2.1688431668
1024 2.162 2.158 2.167 => 2.16208440826
2048 2.207 2.176 2.213 => 2.19829998424
4096 2.180 2.196 2.202 => 2.19291917834
8192 2.173 2.215 2.188 => 2.19207065277
16384 2.258 2.232 2.249 => 2.24609975704
32768 2.262 2.251 2.274 => 2.26239771771
65536 2.298 2.264 2.246 => 2.26917420394
131072 2.285 2.266 2.313 => 2.28767871168
262144 2.351 2.333 2.366 => 2.35030805124
524288 2.932 2.816 2.834 => 2.86047313113
1048576 3.312 3.343 3.326 => 3.32721167007
2097152 3.461 3.451 3.547 => 3.48622758473
4194304 3.479 3.503 3.547 => 3.50964316455
8388608 3.733 3.496 3.532 => 3.58716466865
16777216 3.583 3.522 3.569 => 3.55790996695
33554432 3.550 3.556 3.512 => 3.53952594744
Von ungefähr 2,8 Sekunden für ein einzelnes (wiederholtes) Element fällt es für 2, 4, 8, 16, ... verschiedene Elemente auf ungefähr 2,2 Sekunden ab und bleibt bei ungefähr 2,2 Sekunden bis zu den Hunderttausenden. Ich denke, dies verwendet meinen L2-Cache (4 × 256 KB, ich habe einen i7-6700 ).
Dann steigen die Zeiten in wenigen Schritten auf 3,5 Sekunden. Ich denke, dies verwendet eine Mischung aus meinem L2-Cache und meinem L3-Cache (8 MB), bis dies ebenfalls "erschöpft" ist.
Am Ende bleibt es bei ungefähr 3,5 Sekunden, denke ich, weil meine Caches bei den wiederholten Elementen nicht mehr helfen.