Theo giải thích của những người khác, nó không chỉ sao chép các tham chiếu mà còn tăng số lượng tham chiếu bên trong các đối tượng và do đó các đối tượng được truy cập và bộ nhớ cache đóng một vai trò.
Ở đây tôi chỉ muốn thêm nhiều thử nghiệm hơn. Không quá nhiều về xáo trộn và không bị xáo trộn (trong đó việc truy cập một phần tử có thể bỏ lỡ bộ nhớ cache nhưng đưa các phần tử sau vào bộ nhớ cache để chúng bị tấn công). Nhưng về phần tử lặp lại, trong đó các lần truy cập sau của cùng một phần tử có thể chạm vào bộ nhớ cache vì phần tử vẫn còn trong bộ nhớ cache.
Kiểm tra một phạm vi bình thường:
>>> from timeit import timeit
>>> a = range(10**7)
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[5.1915339142808925, 5.1436351868889645, 5.18055115701749]
Một danh sách có cùng kích thước nhưng chỉ có một phần tử được lặp đi lặp lại nhiều lần sẽ nhanh hơn vì nó luôn truy cập vào bộ nhớ cache:
>>> a = [0] * 10**7
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[4.125743135926939, 4.128927210087596, 4.0941229388550795]
Và dường như không quan trọng nó là số nào:
>>> a = [1234567] * 10**7
>>> [timeit(lambda: list(a), number=100) for _ in range(3)]
[4.124106479141709, 4.156590225249886, 4.219242600790949]
Thật thú vị, nó thậm chí còn nhanh hơn khi tôi lặp lại hai hoặc bốn phần tử giống nhau:
>>> 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]
Tôi đoán có điều gì đó không thích cùng một bộ đếm duy nhất tăng lên liên tục. Có thể một số đường ống bị đình trệ bởi vì mỗi lần tăng phải chờ kết quả của lần tăng trước, nhưng đây là một phỏng đoán hoang đường.
Dù sao, hãy thử điều này cho số lượng lớn hơn các phần tử lặp lại:
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
Kết quả đầu ra (cột đầu tiên là số phần tử khác nhau, cho mỗi phần tử tôi kiểm tra ba lần và sau đó lấy giá trị trung bình):
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
Vì vậy, từ khoảng 2,8 giây cho một phần tử duy nhất (lặp lại) nó giảm xuống khoảng 2,2 giây cho 2, 4, 8, 16, ... các phần tử khác nhau và giữ nguyên ở khoảng 2,2 giây cho đến hàng trăm nghìn. Tôi nghĩ rằng điều này sử dụng bộ nhớ cache L2 của tôi (4 × 256 KB, tôi có i7-6700 ).
Sau đó, qua một vài bước, thời gian lên đến 3,5 giây. Tôi nghĩ rằng điều này sử dụng kết hợp bộ nhớ cache L2 và bộ nhớ cache L3 của tôi (8 MB) cho đến khi nó "cạn kiệt".
Cuối cùng, nó vẫn ở mức khoảng 3,5 giây, tôi đoán vì bộ nhớ đệm của tôi không giúp ích gì với các phần tử lặp lại nữa.