在PYTHON中使用StringIO的性能提升實測(更新list-join對比)


剛開始學習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行。實測結果如下:

clip_image008

可以看到,在應用了內存文件后,程序的處理性能有了翻天覆地的變化,處理耗時為前者的將近千分之一,贊!!!

 

最近又嘗試修改字符串連接為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行。實測結果如下:

image

可以看到,在應用了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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM