簡體   English   中英

有沒有更快的方法來在python中進行while循環?

[英]Is there a faster way to do a while loop in python?

我只花了一個小時仔細檢查我的代碼,以找出為什么我重寫了一大堆代碼后仍會陷入困境。 我沒想到循環這么慢。 我想我從來沒有在時間很緊迫的地方使用過它們。

我最終將問題縮小到此方法,經過測試,我發現while循環內的兩行運行非常快。 大約是30/100000到1/10000秒,但是當我將datetime調用直接放在while循環之外時,它會減慢到大約1秒。

def query(self, command):
    result = ''
    result_list = []
    self.obd.write(command + "\r\n")
    while result != '>':
        result = self.obd.readline()
        result_list.append(result)
    self.result = result_list[-2].strip()

為什么while循環如此之慢如此緩慢,我將如何加快速度呢?

為了解釋我在做什么,我從一台設備獲取串行輸入,該設備似乎對輸出多少行有自己的看法。 有時我需要的信息在第二行,有時在第三行,有時在第一行。 我只知道那是“>”符號之前的最后一行,而我嘗試過的其他方法使我留下了未讀的緩沖數據,這些數據后來使我感到混亂,因此我必須等待“>”。

編輯:顯然我做得不夠好。

我從上面的代碼開始,然后對其進行了編輯以檢查其運行速度。

def query(self, command):
    result = ''
    result_list = []
    self.obd.write(command + "\r\n")
    while result != '>':
        a = datetime.datetime.now()
        result = self.obd.readline()
        result_list.append(result)
        b = datetime.datetime.now()
        print b - a
    self.result = result_list[-2].strip()

每次運行此方法,平均運行時間少於1/10000秒。

def query(self, command):
    result = ''
    result_list = []
    self.obd.write(command + "\r\n")
    a = datetime.datetime.now()
    while result != '>':
        result = self.obd.readline()
        result_list.append(result)
    b = datetime.datetime.now()
    print b - a
    self.result = result_list[-2].strip()

每次運行此方法都需要1+秒。

while循環內部發生的事情是正在讀取串行端口。 如果我在它周圍進行for循環,它會工作一段時間,然后在緩沖區落后一點時停止,但是我可以查詢最大60 Hz的端口。

如果不是while循環,為什么我得到所看到的結果?

需要澄清的是,與所有解釋性語言(如Python)的循環相比,其在編譯語言(如c)中的實現速度非常慢。 對於for循環和其他循環也是如此。 原因是解釋性語言每個語句的開銷很大,而大多數編譯語言卻很少或根本沒有這樣的開銷。 即使使用列表推導,此開銷也會在循環的每次迭代中產生。 至少有三種方法可以優化或緩解解釋性語言中的循環:

  • 優化每個循環迭代,以蠻橫地加快運行時間。
  • 使用針對任務進行了優化的內置操作。
  • 使用具有“矢量化”功能的庫,例如numpy可用的庫。 (讀取/寫入/操作數字數據時的最佳解決方案。)這些庫通常部分地使用編譯后的代碼組成,以加快重復操作的速度。

在您的情況下,我建議第一個(通過僅存儲標量而不是數組來優化內部):

def query(self, command):
    result = ''
    line = ''
    self.obd.write(command + "\r\n")
    while line != '>':
        result = line
        line = self.obd.readline()
    self.result = result.strip()

append函數比簡單的標量分配要花費更多的時間,因此節省了一點時間,並且您的代碼已經忽略了倒數第二行。

或者,您可以嘗試使用優化的內置函數。 如果obd支持readline() ,則很有可能是類似文件的文件,也將支持readlines()read() 根據數據的長度和復雜性,將re.searchread()的結果結合使用有時會更快:

def query(self, command):
    self.obd.write(command + "\r\n")
    result = re.search('(?:^|\n)([^\n]*?)\n>(\n|$)', obd.read())
    self.result = result.group(1) if result else None

正則表達式並不像看起來那么復雜。 它僅搜索第二行,其后是等於> 它也不是非常有效。

最后一種方法是使用非正則表達式內置函數來減少while循環必須運行的次數:

def query(self, command):
    self.obd.write(command + "\r\n")
    remaining = obd.read().lstrip()
    sep = ''
    while remaining and remaining[0] != '\n':
        chunk, sep, remaining = remaining.partition('\n>')
    self.result = chunk.rpartition('\n')[2] if sep else ''

對於行開始處的每個> ,這只會運行一次,而可能完全是一次。

請注意,后兩個更改(正則表達式和使用分區)都依賴於首先讀取整個文件。 有兩個副作用需要注意:

  • 讀取整個文件所需的內存與將整個文件添加到列表一樣多,因此與以前的方法相比,沒有節省內存,只有時間。
  • 因為讀取了整個文件,所以也讀取了> 之后的字節/行,並且如果obd不發送EOF信號(例如,它是未關閉的管道),它將失敗。 請注意,特別是如果您打算讓另一個文件繼續從obd讀取時。

雖然循環並不慢。 實際上,while循環的開銷實際上是不可察覺的。 如果您認為語句在循環外快速運行,則必須關閉測量。

從文件或串行設備讀取數據是您可以做的最慢的事情之一。 由於您的while循環中包含readline語句,因此可能正在減慢速度。 也許它正在等待一行輸入,而這種等待正在減慢它的速度。

您的問題提到將datetime調用從循環內部移動到外部,但是我在任何地方都看不到任何datetime函數,因此很難推測這是否是問題的一部分。

我不確定到底是什么問題,這很令人沮喪,但還不足以讓我分清別人的代碼。 我終於得到了一個可行的解決方案。

我決定不使用readline(),而是使用read(1),它在每次調用時從緩沖區讀取一個字節。 這樣,我就可以等待“>”字符,然后返回上一行。

這是新方法:

def query(self, command):
    line = ''
    self.obd.write(command + "\r\n")
    while True:
        c = self.obd.read(1)
        line += c
        if c == '>':
            break
    # should work even if there is no newline character
    self.result = line.split('\r')[-2].strip()

這與我使用for循環使用的先前方法所花費的時間相同。 〜60hz,但不太可能使緩沖區充滿垃圾。

感謝您的所有幫助。 它使我步入正軌。

暫無
暫無

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

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