Рассмотрим следующий цикл:
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. subprocess.Popen.
Это становится еще более странным. Можно подумать, что затрагивается только первая команда, выполняемая после 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 запусков:
Больше замечаний:
- Мы получаем тот же эффект при замене
subprocess.Popen()("command 1") наtime.sleep()или rawkits libraw C++ для инициализации привязок (libraw.bindings.LibRaw()). Однако использование других библиотек с привязками C++, таких как libraw.py или OpenCVscv2.warpAffine(), не влияет на время выполнения. Открытие файлов тоже не происходит. - Эффект не вызван
time.time(), потому что он виден с помощьюtimeit.timeit()и даже измерением вручную, когда появляется результатprint(). - Это также происходит без цикла for.
- Это происходит даже тогда, когда между "командой 1" (
subprocess.Popen) и "командой 2" выполняется много разных (возможно, CPU- и занимающих много памяти) операций. - С массивами Numpy замедление оказывается пропорциональным размеру массива. При относительно больших массивах (~ 60 M точек) простая операция
arr += 1может занять до 300 мс!
Вопрос: Что может вызвать этот эффект и почему он влияет только на текущую итерацию цикла?
Я подозреваю, что это может быть связано с переключением контекста, но это, похоже, не объясняет, почему повлияла бы итерация всего цикла. Если переключение контекста действительно является причиной, почему некоторые команды запускают его, а другие нет?

