Рассмотрим следующий цикл:
for i in range(20):
if i == 10:
subprocess.Popen(["echo"]) # command 1
t_start = time.time()
1+1 # command 2
t_stop = time.time()
print(t_stop - t_start)
Команда «команда 2» выполняется систематически дольше, когда перед ней запускается «команда 1». На следующем графике показано время выполнения 1+1
в зависимости от индекса цикла i
, усредненного по 100 циклам.
Выполнение 1+1
выполняется в 30 раз медленнее, когда ему предшествует subprocess.Popen
.
1+1 as a function of loop index">
Становится еще страннее. Можно подумать, что затрагивается только первая команда, запускаемая после subprocess.Popen()
, но это не так. Следующий цикл показывает, что все команды в текущей итерации цикла затрагиваются . Но последующие итерации циклов кажутся в основном нормальными.
var = 0
for i in range(20):
if i == 10:
# command 1
subprocess.Popen(['echo'])
# command 2a
t_start = time.time()
1 + 1
t_stop = time.time()
print(t_stop - t_start)
# command 2b
t_start = time.time()
print(1)
t_stop = time.time()
print(t_stop - t_start)
# command 2c
t_start = time.time()
var += 1
t_stop = time.time()
print(t_stop - t_start)
Вот график времени выполнения для этого цикла, в среднем более 100 запусков:
1+1, print(1), and var += 1 as a function of loop index">
Дополнительные замечания:
- Мы получаем тот же эффект при замене
subprocess.Popen()
(«команда 1») на time.sleep()
или rawkit libraw C ++ инициализация привязок (libraw.bindings.LibRaw()
). Однако использование других библиотек с привязками C ++, таких как libraw.py или OpenCV cv2.warpAffine()
, не влияет на время выполнения. Не открывать файлы тоже.
- Эффект не вызван
time.time()
, потому что он виден с timeit.timeit()
, и даже при измерении вручную, когда появляется print()
результат.
- Это также происходит без цикла for.
- Это происходит даже тогда, когда между «командой 1» (
subprocess.Popen
) и «командой 2» выполняется много разных (возможно, занимающих ЦП и памяти) операций.
- В массивах Numpy замедление представляется пропорциональным размеру массива. С относительно большими массивами (~ 60M точек) простая операция
arr += 1
может занять до 300 мс!
Вопрос: Что может вызвать этот эффект и почему он влияет только на текущую итерацию цикла?
Я подозреваю, что это может быть связано с переключением контекста, но, похоже, это не объясняет, почему повлияла итерация всего цикла. Если переключение контекста действительно является причиной, почему некоторые команды запускают его, а другие нет?