2017-02-07 14 views
0

Несмотря на то, что я работаю над довольно большим проектом Python (3), я столкнулся с этим странным поведением, когда итерация через словарь будет происходить очень редко (< 0,1% времени) времени дольше, чем обычно.Почему синтаксический анализ dict очень часто занимает гораздо больше времени, чем обычно.

Вот очень минимальный пример, который показывает поведение:

from time import perf_counter 
    from statistics import mean 

    d = {"1": 0} 
    times = [] 

    for _ in range(1000000): 
     start = perf_counter() 
     d = {k: v for k, v in d.items()} 
     diff = (perf_counter() - start) * 10e6 
     if diff > 0: 
      times.append(diff) 

    print("Mean: {}".format(mean(times))) 
    print("Max: {}".format(max(times))) 

Например. запуск этого однажды дает мне среднее время 10,806199298240244 микросекунд, но максимальное время составляет 31015,980057418346 микросекунд, что примерно в 3000 раз больше, чем обычно.

Есть ли способ избежать этого поведения или это присуще Python?

Edit: Timed с помощью perf_counter() вместо DateTime

+2

FWIW, я не могу сказать, действительно ли это явление реально. Однако использование 'datetime.now()' для синхронизации чего-то (особенно чего-то такого небольшого), вероятно, будет очень неточным. 'timeit.default_timer', вероятно, станет намного лучшим местом для понимания этих явлений ... – mgilson

+0

Попробуйте отключить сбор мусора для теста? – Ryan

ответ

3

Большинство словарных действий создания вы совершили потребовалось всего несколько наносекунд - и лечились в 0 микросекунд. В результате каждое действие, которое имело некоторую задержку, даже если задержка составляло несколько микросекунд, считается огромным изменением - и каждый результат 0 снижает среднее значение из его границ.

Кроме того, максимальные результаты отличаются высоким уровнем от 0 до.


Попробуйте

from time import perf_counter 
from statistics import mean, stdev 

d = {"1": 0} 
times = [] 

for _ in range(100000): 
    start = perf_counter() 
    d = {k: v for k, v in d.items()} 
    diff = (perf_counter() - start) * 10e6 
    if diff > 0: 
     times.append(diff) 

print("Mean: {}".format(mean(times))) 
print("Stddev: {}".format(stdev(times))) 
print("Max: {}".format(max(times))) 

и вы должны получить менее экстремальные различия - и гораздо меньшие результаты, чем при использовании time.time или datetime.now (также, посмотрите на стандартное отклонение):

Mean: 11.520527719010078 
Stddev: 4.476865528640042 
Max: 443.1869339160954 
+0

'time.perf_counter' или' timeit.default_timer', вероятно, следует использовать вместо 'time.time'. Кроме того, вероятно, неплохо использовать 'timeit' для выполнения всего этого, поскольку он обрабатывает часто забытые детали, такие как выключение сборщика мусора (что вполне возможно является виновником нескольких длинных циклов здесь). К сожалению, я не знаю, как это сделать, чтобы дать вам гистограмму фактических результатов ... – mgilson

+0

@mgilson Я считаю, что matplotlib может – Uriel

+0

@mgilson изменен на 'perf_counter' - результаты более точны и намного меньше. благодаря! – Uriel