为什么Python操作是30×;调用time.sleep或subprocess.Popen后速度变慢?
考虑以下循环:为什么Python操作是30×;调用time.sleep或subprocess.Popen后速度变慢?,python,performance,subprocess,python-performance,Python,Performance,Subprocess,Python Performance,考虑以下循环: 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) 当“命令1”在“命令2”之前运行时,“命令2”命令系统性地运行时间更长。下图显示了1+1的执行时间,作为循环索引i的函
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)
当“命令1”在“命令2”之前运行时,“命令2”命令系统性地运行时间更长。下图显示了1+1
的执行时间,作为循环索引i
的函数,平均运行100次
前面有子进程.Popen
时,执行1+1
的速度要慢30倍
更奇怪的是。有人可能认为只有在
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次运行:
更多备注:
- 在使用<代码> >时间>睡眠()>代码>或C++绑定初始化(<代码> Labo.Buang.SabangWar()/代码>)时,我们得到了相同的效果。但是,使用其他的带有C++绑定的库,例如OpenCV的代码> CV2..WalPAffIn()/<代码>不会影响执行时间。打开文件也不起作用
- 这种影响不是由
引起的,因为它可以通过time.time()
看到,甚至可以在出现timeit.timeit()
结果时手动测量print()
- 在没有for循环的情况下也会发生这种情况
- 即使在“command 1”(
)和“command 2”之间执行了大量不同的(可能是CPU和内存消耗)操作,也会发生这种情况subprocess.Popen
- 对于Numpy阵列,速度减慢似乎与阵列的大小成正比。具有相对较大的阵列(~60 M点),一个简单的
操作可能需要300毫秒李>arr+=1
问题:什么可能导致这种效果,为什么它只影响当前循环迭代
我怀疑这可能与上下文切换有关,但这似乎无法解释为什么整个循环迭代会受到影响。如果上下文切换确实是原因,为什么有些命令会触发它,而其他命令不会触发它?我的猜测是,这是由于Python代码被从CPU/内存系统中的各种缓存中逐出所致
perflib
包可用于提取有关缓存状态的更详细的CPU级别统计信息,即命中/未命中数
在调用Popen()
后,我得到~5倍的LIBPERF\u COUNT\u HW\u CACHE\u MISSES
计数器:
from subprocess import Popen, DEVNULL
from perflib import PerfCounter
import numpy as np
arr = []
p = PerfCounter('LIBPERF_COUNT_HW_CACHE_MISSES')
for i in range(100):
ti = []
p.reset()
p.start()
ti.extend(p.getval() for _ in range(7))
Popen(['echo'], stdout=DEVNULL)
ti.extend(p.getval() for _ in range(7))
p.stop()
arr.append(ti)
np.diff(np.array(arr), axis=1).mean(axis=0).astype(int).tolist()
给我:
2605, 2185, 2127, 2099, 2407, 2120,
5481210,
16499, 10694, 10398, 10301, 10206, 10166
(在非标准位置断开表示代码流的行)为了记录在案,我已经能够用Python 3.7重现这一点,使用
time.perf_counter_ns()
而不是time.time()
,使用1+1
甚至在time.perf_counter_ns()的计数器之间什么都不做
…为了确保我理解,你的意思是因为新进程接管了处理器的缓存,对吗?(当然,粗略地说。)@jpmc26有点……做任何事情都会给缓存带来压力,你做的工作越多,移动的东西就越多。这很可能会导致旧的东西从缓存中被逐出,当这些旧的东西再次被需要时,它们需要重新填充。启动一个进程需要消耗几MB的RAM,创建一个大的numpy
阵列等等。