Başkaları tarafından açıklandığı gibi, bu sadece referanslar kopyalayarak değil, aynı zamanda nesnelerin içindeki başvuru sayıları artar ve böylece nesneler değil edilir erişilen ve önbellek bir rol oynar.
Burada sadece daha fazla deney eklemek istiyorum. Karıştırılmış ve karıştırılmamış (burada bir öğeye erişmek önbelleği kaçırabilir, ancak aşağıdaki öğeleri önbelleğe alarak vurulmaları için). Ancak, aynı öğeye daha sonraki erişimlerin, öğe hala önbellekte olduğu için önbelleğe çarpabileceği yinelenen öğeler hakkında.
Normal bir aralığı test etmek:
>>> from timeit import timeit
>>> a = range(10**7)
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[5.1915339142808925, 5.1436351868889645, 5.18055115701749]
Aynı boyutta ancak tek bir öğenin defalarca tekrarlandığı bir liste daha hızlıdır çünkü önbelleğe her zaman ulaşır:
>>> a = [0] * 10**7
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[4.125743135926939, 4.128927210087596, 4.0941229388550795]
Ve kaç numara olduğu önemli değil:
>>> a = [1234567] * 10**7
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[4.124106479141709, 4.156590225249886, 4.219242600790949]
İlginç bir şekilde, aynı iki veya dört öğeyi tekrarladığımda daha da hızlanıyor:
>>> 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]
Sanırım aynı tek sayaçtan hoşlanmayan bir şey her zaman arttı. Belki bir miktar boru hattı durur çünkü her artış bir önceki artışın sonucunu beklemek zorundadır, ancak bu çılgın bir tahmin.
Her neyse, bunu daha fazla sayıda tekrarlanan öğe için denemek:
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
Çıktı (ilk sütun farklı öğelerin sayısıdır, her biri için üç kez test edip ortalamayı alırım):
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
Yani tek bir (tekrarlanan) element için yaklaşık 2.8 saniyeden, 2, 4, 8, 16, ... farklı elementler için yaklaşık 2.2 saniyeye düşer ve yüzbinlere kadar yaklaşık 2.2 saniyede kalır. Bunun L2 önbelleğimi kullandığını düşünüyorum (4 × 256 KB, i7-6700'üm var ).
Daha sonra birkaç adımda süre 3,5 saniyeye çıkar. Sanırım bu, "bitene" kadar L2 önbelleğim ve L3 önbelleğimin (8 MB) bir karışımını kullanıyor.
Sonunda yaklaşık 3,5 saniyede kalıyor, çünkü önbelleklerim artık tekrarlanan öğelere yardımcı olmuyor.