一 前言
最近在开发的数据核对方案中用到了Python标准库Difflib,本来它工作的挺符合预期,可当它遇到那个文件,仿佛遇到了克星,那文件才100行*77列的数据,经它对比,居然耗时61s。这是无法接受的,因为后续线上流量抽取比对,绝非这点量级。该怎么破?
二 重现现象
以下是使用Difflib比对那个文件,数据量是100行*77列,耗时61s,如下:
好吧,那就降低数据量到5行*77列,看看效果,耗时只有0.05s,如下:
从耗时结果上,不难发现,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。