在PYTHON中使用StringIO的性能提升实测(更新list-join对比)

时间:2024-09-04 08:36:56

刚开始学习PYTHON,感觉到这个语言真的是很好用,可以快速完成功能实现。

最近试着用它完成工作中的一个任务:Linux服务器中完成对.xml.gz文件的解析,生成.csv文件,以供SqlServer服务器导入,做进一步的数据分析。

解压后的xml文件格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<bulkPmMrDataFile>
<fileHeader fileFormatVersion="V1.0.4" reportTime="2016-02-22T21:00:00.000" ></fileHeader>
<eNB id="122941">
<measurement>
<smr>MR.LteScRSRP MR.LteScRSRQ MR.LteScTadv MR.LteSceNBRxTxTimeDiff ...</smr>
<object id="31472897" MmeUeS1apId="2417300878" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:02.517">
<v>38 22 6 31 34 0 34 38400 110 NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL 7 23 NIL 26 24 0 0 </v>
</object>
<object id="31472897" MmeUeS1apId="2416229278" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:02.792">
<v>61 30 5 32 45 0 27 38400 110 NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL 7 14 NIL 1 49 0 0 </v>
</object>
<object id="31472897" MmeUeS1apId="2148286566" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:07.177">
<v>42 26 5 31 30 412 35 38400 110 NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL 7 14 NIL 26 24 0 0 </v>
</object>
<object id="31472897" MmeUeS1apId="2417300878" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:07.677">
<v>39 22 7 31 35 387 36 38400 110 36 18 38400 108 NIL NIL NIL NIL NIL NIL NIL 5 10 NIL 25 25 0 0 </v>
</object>
<object id="31472897" MmeUeS1apId="2416229278" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:07.962">
<v>61 27 5 31 43 427 36 38400 110 49 4 38400 108 NIL NIL NIL NIL NIL NIL NIL 9 13 NIL 1 49 0 0 </v>
<v>61 27 5 31 43 427 36 38400 110 46 0 38400 295 NIL NIL NIL NIL NIL NIL NIL 9 13 NIL 1 49 0 0 </v>
</object>
<object id="31472897" MmeUeS1apId="269973066" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:08.382">
<v>41 23 9 32 23 447 36 38400 110 32 11 38400 358 NIL NIL NIL NIL NIL NIL NIL 6 22 NIL 24 26 0 0 </v>
<v>41 23 9 32 23 447 36 38400 110 31 7 38400 295 NIL NIL NIL NIL NIL NIL NIL 6 22 NIL 24 26 0 0 </v>
<v>41 23 9 32 23 447 36 38400 110 28 8 38400 460 NIL NIL NIL NIL NIL NIL NIL 6 22 NIL 24 26 0 0 </v>
</object>
</measurement>
<measurement>
<smr>MR.LteScPlrULQci1 MR.LteScPlrULQci2 MR.LteScPlrULQci3 MR.LteScPlrULQci4 ...</smr>
<object id="60057088" MmeUeS1apId="2416982066" MmeGroupId="1025" MmeCode="206" TimeStamp="2016-02-24T06:00:03.962">
<v>NIL NIL NIL NIL NIL NIL NIL NIL 0 NIL NIL NIL NIL NIL NIL NIL NIL 0 </v>
</object>
..........................
</measurement>
<measurement>
<smr>MR.LteScRIP</smr>
<object id="60057089:37900:2" TimeStamp="2016-02-24T06:00:11.259" MmeCode="NIL" MmeGroupId="NIL" MmeUeS1apId="NIL">
<v>81</v>
</object>
..........................
</measurement>
</eNB>
</bulkPmMrDataFile>

廖雪峰博客学习得知目前常用的两种xml解析方式为DOM和SAX,分别进行了尝试,从个人角度来看,还是DOM比较好用,包括应用逻辑以及代码编写,都相对轻松简单。

在尝试SAX解析的过程中,开始按照习惯的方式,对XML文件中筛选的可用字段进行字符串相加,导致的后果是程序运行特别的慢,于是又尝试了cStringIO内存文件,这才感受到了内存文件的速度优势。

下面列出其中的关键字段以作对比。

  • 使用字符串相加方式(s为定义的字符串变量):
#处理开始标签
def start_element(self, name, attrs):
global d_eNB
global d_obj
global s
if name == 'eNB':
d_eNB = attrs
elif name == 'object':
d_obj = attrs
elif name == 'v':
s = s + d_eNB['id']+' '+ d_obj['id']+' '+d_obj['MmeUeS1apId']+' '+d_obj['MmeGroupId']+' '+d_obj['MmeCode']+' '+d_obj['TimeStamp']+' '
else:
pass
  • 使用cStringIO内存文件方式(output为定义的cStringIO变量):
#处理开始标签
def start_element(self, name, attrs):
global d_eNB
global d_obj
if name == 'eNB':
d_eNB = attrs
elif name == 'object':
d_obj = attrs
elif name == 'v':
output.write(d_eNB['id']+' '+ d_obj['id']+' '+d_obj['MmeUeS1apId']+' '+d_obj['MmeGroupId']+' '+d_obj['MmeCode']+' '+d_obj['TimeStamp']+' ')
else:
pass

对于一个相同的.xml.gz文件,文件总行数为173890行,其中有用数据为90079行。实测结果如下:

在PYTHON中使用StringIO的性能提升实测(更新list-join对比)

可以看到,在应用了内存文件后,程序的处理性能有了翻天覆地的变化,处理耗时为前者的将近千分之一,赞!!!

最近又尝试修改字符串连接为list-join方式,代码如下:

  • 使用list-join方式(lst为定义的list变量):
#处理开始标签
def start_element(self, name, attrs):
global d_eNB
global d_obj
global lst
if name == 'eNB':
d_eNB = attrs
elif name == 'object':
d_obj = attrs
elif name == 'v':
lst.append(d_eNB['id']+' ')
lst.append(d_obj['id']+' ')
lst.append(d_obj['MmeUeS1apId']+' ')
lst.append(d_obj['MmeGroupId']+' ')
lst.append(d_obj['MmeCode']+' ')
lst.append(d_obj['TimeStamp']+' ')
#列表连接
t.writelines(''.join(lst).replace(' \n','\r\n').replace(' ',',').replace('T',' ').replace('NIL','')) #写入解析后内容

对于一个相同的.xml.gz文件,文件总行数为173890行,其中有用数据为90079行。实测结果如下:

在PYTHON中使用StringIO的性能提升实测(更新list-join对比)

可以看到,在应用了list-join方式后,处理用时和StringIO方式相当,说明利用list-join方式也能够带来和StringIO相同的性能提升。

因此,推荐在编程中要避免使用过多的字符串相加,而要以list-join和StringIO代替之。

至于为什么字符串相加效率要比其他两种低那么多呢,其实我也不是很懂,但是我会搜索啊。。。

谁说python字符串相加效率低

这个看情况分析,官方文档当中也有说,相加产生的str是immutable的,如果只是两个字符串相加,并没有什么问题,但是如果是n>>1个字符串相加,这样中间会产生n-1个中间值,这些中间值都是immutable的,所以之后就是创建一个释放一个再创建下一个释放下一个。
而join在对于n个字符串相加过程中内部实现直接全部相连,就没有这种中间值了。

如果你相加的字符串不多,用加号还是更加方便的,另一方面,你选择了用Python,还真的在乎那一两秒的效率吗?

测试用的程序和文件可以到我的github页面下载,欢迎小伙伴们一起学习讨论。

xml_parser_str.py、xml_parser_list.py、xml_parser_StringIO.py

测试用例:../mro