Как объясняется другими, это не просто копирование ссылок, но также увеличивает количество отсчетов ссылок внутри объектов, и, таким образом, объекты имеют доступ к, и кеш играет определенную роль.
Здесь я просто хочу добавить еще несколько экспериментов. Не столько о перетасовке и неподготовленности (когда доступ к одному элементу может пропустить кеш, но в кеш-память входят следующие элементы, чтобы они попадали). Но о повторяющихся элементах, где более поздние обращения одного и того же элемента могут попасть в кеш, потому что элемент все еще находится в кеше.
Тестирование нормальный диапазон:
>>> from timeit import timeit
>>> a = range(10**7)
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[5.1915339142808925, 5.1436351868889645, 5.18055115701749]
список того же размера, но с только один элемент повторяется снова и снова быстрее, потому что она попадает в кэш все время:
>>> a = [0] * 10**7
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[4.125743135926939, 4.128927210087596, 4.0941229388550795]
И не имеет значения, какое это количество:
>>> a = [1234567] * 10**7
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[4.124106479141709, 4.156590225249886, 4.219242600790949]
Интересно, что это становится еще быстрее когда я вместо того, чтобы повторять одни и те же два или четыре элемента:
>>> 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]
Я думаю, что-то не нравится, то же один счетчик увеличился все время. Может быть, некоторые pipeline stall, потому что каждое увеличение должно ждать результата предыдущего увеличения, но это дикая догадка.
Во всяком случае, пытаются это для еще большего числа повторяющихся элементов:
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
выход (первый столбец является число различных элементов, для каждого я тест три раза, а затем взять среднее):
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
Таким образом, примерно за 2,8 секунды для одного (повторного) элемента он опускается до 2,2 секунды для 2, 4, 8, 16, ... разных элементов и остается около 2,2 секунды до сотен тысяч. Я думаю, что это использует мой кеш L2 (4 × 256 КБ, у меня есть i7-6700).
Затем, за несколько шагов, время увеличивается до 3,5 секунд. Я думаю, что это использует смесь моего кэша L2 и моего кеша L3 (8 МБ), пока это не «исчерпано».
В конце концов он остается около 3,5 секунд, я думаю, потому что мои кеши больше не помогают с повторяющимися элементами.
Для устранения «внешнего воздействия» (например, внутреннего состояния интерпретатора Python, эвристики кеширования базовой архитектуры HW и т. Д.) - попробуйте поменять эти два теста (т. Е. Изменить порядок их выполнения) и убедитесь, что ваши измерения согласованы. –
Я попробовал это сам, и их порядок, кажется, влияет на измерения. Поэтому я бы предположил, что это имеет какое-то отношение к внутреннему состоянию интерпретатора Python. –
@barakmanos Это были отдельные прогоны скрипта. Кроме того, я уже делал по пять трасс, чтобы попытаться устранить другие удары. И да, я получаю это последовательно. –