一 前言html
最近在開發的數據覈對方案中用到了Python標準庫Difflib,原本它工做的挺符合預期,可當它遇到那個文件,彷彿遇到了剋星,那文件才100行*77列的數據,經它對比,竟然耗時61s。這是沒法接受的,由於後續線上流量抽取比對,絕非這點量級。該怎麼破?python
二 重現現象微信
如下是使用Difflib比對那個文件,數據量是100行*77列,耗時61s,以下:app
好吧,那就下降數據量到5行*77列,看看效果,耗時只有0.05s,以下:ide
從耗時結果上,不難發現,Difflib在這個文件的比對性能較差,並且耗時不隨數據量線性增長,這是最恐怖的地方,若是繼續增大數據量,耗時將會變得沒法忍受。性能
三 優化思路學習
Difflib做爲標準庫,它的功能只是比對數據,而後生成各樣結果格式。當遇到耗時嚴重這類問題,首先應該從本身的數據上入手,個人優化思路有兩個:測試
第一, 過濾掉相同的行數據,下降比對數據量;大數據
第二, 數據分片;優化
針對第一個思路,將文件以行分割存放到列表,而後將列表相同位置的相同數據刪除掉,只剩下不一樣的行數據,這樣作的好處很明顯,一方面能夠下降比對的數據量,提高效率,另外,輸出的結果也更乾淨,不會再輸出無必要的相同行數據;
針對第二個思路,將待比對的數據劃分紅一個個相對較小的數據塊,實現快速比對,這個方法的可行性,能夠從上述數據量耗時比對得出。
四 具體實現
過濾相同行優化策略,實現代碼以下:
# 過濾相同行 source_length = len(source) # source爲原始數據按行分割的列表 target_length = len(target) # target爲目標數據按行分割的列表 min_length = source_length if source_length < target_length else target_length pos_list = [] # 標記相同行的行號,保留列頭 for index in range(1, min_length): # 注意保序 if operator.eq(source[index], target[index]): pos_list.append(index) # 刪除相同行數據, 注意索引漂移 source = [source[index] for index in range(source_length) if index not in pos_list] target = [target[index] for index in range(target_length) if index not in pos_list]
數據分片優化策略,實現代碼以下:
# 分片 max_length = source_length if source_length > target_length else target_length # 用於分片 # 分片,注意保證不能漏行 start_pos = 0 step = 10 # 分片大小,即單次比對行數,默認10行 end_pos = start_pos + step diff = difflib.HtmlDiff() # 建立htmldiff實例對象 while end_pos < max_length + step: detail_info = diff.make_file(source[start_pos: end_pos], target[start_pos: end_pos]) # 處理邏輯
五 優化結果
在僅使用數據分片的優化策略的狀況下,比對那個100行*77列的文件,結果顯示比對耗時僅1.8s。而正如上文所述,優化前比對該文件的耗時爲61s,更重要的是,由於數據分片,每片比對耗時基本穩定,即便數據量繼續增大,耗時也只是線性增長,而再也不是相似指數型增長。另外,若是疊加過濾數據的第一種策略,相信隨着數據量的降低,耗時數據會有更好的表現。但爲了更直觀的比對相同數據量下優化先後的效果,因此,在此只是使用數據分片的策略。
優化後耗時結果以下:
六 其餘
本文針對Python標準庫Difflib比對文件時遇到的耗時嚴重問題,提出了兩種優化策略,並經測試驗證有效,即僅在數據分片策略下,一樣文件的比對耗時從原先的61s降到1.8s,且耗時只是線性增長。若是有更好的方法,歡迎留言、交流。
關於python學習、分享、交流,筆者開通了微信公衆號【小蟒社區】,感興趣的朋友能夠關注下,歡迎加入,創建屬於咱們本身的小圈子,一塊兒學python。