[英]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.search
與read()
的結果結合使用有時會更快:
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.