簡體   English   中英

為什么 Python 操作在調用 time.sleep 或 subprocess.Popen 后慢了 30 倍?

[英]Why are Python operations 30× slower after calling time.sleep or subprocess.Popen?

考慮以下循環:

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”命令需要系統地更長的時間來運行。 下圖顯示了1+1的執行時間作為循環索引i的函數,平均超過 100 次運行。

的執行1+1是慢30倍之前時subprocess.Popen

<code>1+1</code> 的執行時間作為循環索引的函數


它變得更加奇怪。 有人可能認為只有在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 次運行:

<code>1+1、print(1) 和 var += 1</code> 的執行時間作為循環索引的函數


更多備注:

  • 更換時,我們得到相同的效果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”之間執行了許多不同的(可能是 CPU 和內存消耗的)操作時,也會發生這種情況。
  • 對於 Numpy 數組,減慢似乎與數組的大小成正比。 對於相對較大的數組(約 60 M 點),一個簡單的arr += 1操作可能需要長達 300 毫秒!

問題:什么可能導致這種效果,為什么它只影響當前循環迭代?

我懷疑它可能與上下文切換有關,但這似乎並不能解釋為什么整個循環迭代會受到影響。 如果上下文切換確實是原因,為什么有些命令會觸發它而其他命令則不會?

我的猜測是,這是由於 Python 代碼從 CPU/內存系統中的各種緩存中被逐出

perflib包可用於提取有關緩存狀態的更詳細的 CPU 級別統計信息 — 即命中/未命中數。

LIBPERF_COUNT_HW_CACHE_MISSES Popen()調用后,我得到了大約 5 倍的LIBPERF_COUNT_HW_CACHE_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

(在非標准地方打斷的線條表示代碼流程)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM