2014-09-23 48 views
1

好的,所以這個文件是410k行代碼。現在我在1.4秒內解析它,但我需要它更快。有幾個關於這個文件,雖然奇怪的事情......如何使用線程來幫助解析大文件

該文件的結構是這樣的(感謝ARM):ARM fromelf
基本上我解析這一切變成地圖,關鍵是結構的名稱,在這種情況下,由於ARM生成警告,可能會重複。這種情況下的值是後面的字段。

有沒有一種方法可以使用線程將任務分解爲多個線程,將數據添加到相同的地圖?

P.S.沒有找人爲我做這件事,我只是提供了一個文件結構是什麼樣的例子,所以你明白我不能只處理每一行,而是根據結構處理[start:finish]。

根據要求什麼,我分析的樣本:

; Structure, Table , Size 0x104 bytes, from inputfile.cpp 
|Table.TableSize|      EQU 0  ; int 
|Table.Data|        EQU 0x4  ; array[64] of MyClassHandle 
; End of Structure Table 

; Structure, Box2 , Size 0x8 bytes, from inputfile.cpp 
|Box2.|         EQU 0  ; anonymous 
|Box2..|         EQU 0  ; anonymous 
|Box2...Min|        EQU 0  ; Point2 
|Box2...Min.x|       EQU 0  ; short 
|Box2...Min.y|       EQU 0x2  ; short 
|Box2...Max|        EQU 0x4  ; Point2 
|Box2...Max.x|       EQU 0x4  ; short 
|Box2...Max.y|       EQU 0x6  ; short 
; Warning: duplicate name (Box2..) present in (inputfile.cpp) and in (inputfile.cpp) 
; please use the --qualify option 
|Box2..|         EQU 0  ; anonymous 
|Box2...Left|       EQU 0  ; unsigned short 
|Box2...Top|        EQU 0x2  ; unsigned short 
|Box2...Right|       EQU 0x4  ; unsigned short 
|Box2...Bottom|       EQU 0x6  ; unsigned short 
; End of Structure Box2 

; Structure, MyClassHandle , Size 0x4 bytes, from inputfile.cpp 
|MyClassHandle.Handle|     EQU 0  ; pointer to MyClass 
; End of Structure MyClassHandle 

; Structure, Point2 , Size 0x4 bytes, from defects.cpp 
|Point2.x|        EQU 0  ; short 
|Point2.y|        EQU 0x2  ; short 
; End of Structure Point2 

; Structure, __fpos_t_struct , Size 0x10 bytes, from C:\Program Files\DS-5\bin\..\include\stdio.h 
|__fpos_t_struct.__pos|     EQU 0  ; unsigned long long 
|__fpos_t_struct.__mbstate|    EQU 0x8  ; anonymous 
|__fpos_t_struct.__mbstate.__state1|  EQU 0x8  ; unsigned int 
|__fpos_t_struct.__mbstate.__state2|  EQU 0xc  ; unsigned int 
; End of Structure __fpos_t_struct 

END 
+2

[有沒有辦法?](http://cdn.gifbay.com/2012/11/is_such_a_thing_even_possible-9397.gif) – TheBat 2014-09-23 21:48:49

+0

必須看到你在做什麼的例子,線程化的可能性不會因爲Python股票至少有GIL解釋器與之抗衡。所以除非你有一些在隊列後端進行異步C調用,可能運氣不好。你可能能夠將它重構成一個map/reduce的問題,但沒有示例代碼..惡搞喬! – synthesizerpatel 2014-09-23 23:41:24

+0

將文件樣本插入問題會更好。外部鏈接脆弱,有時難以訪問。 – rici 2014-09-23 23:41:46

回答

0

你可能會更好過優化解析器代碼,或者在不同的語言寫它。

在標準Python實現(「CPython」)中,有效多進程的唯一方法是使用multiprocessing模塊,該模塊依賴於使用多個unix進程而不是線程(線程對於計算綁定任務由於全球解釋器鎖定)。您可以使用共享內存對象甚至共享字典(請參閱Managers),但基本上進程間通信的代價非常高,並且很快就會消耗多任務的優勢。

如果您的單個線程在解析過程中不需要有關結構的全局信息,則它們可以分別創建自己的字典,然後您可以合併所有字典到最後。從一個進程向另一個進程發送(可選擇的)Python對象是很容易的,但要考慮以下幾點:你的任務是解析文本表示並創建一個內部表示。酸洗和取消對象包括採取內部表示法,從中產生一個字符串,然後解析通信通道另一端的字符串。換句話說,您的解析任務只是生成另一個解析任務,並帶有一些額外的序列化開銷。這不太可能是一場勝利,除非untooller可能比你寫的更快的解析器。這將我們帶回優化解析器。

並行化問題的一部分通常很簡單,就是在進程之間分割任務。假設該塊被解析(start:finish)是不是太巨大的 - 也就是說,你的410K線組成,比方說,數千名這樣的子任務 - 然後有一個簡單的策略:

  1. 查找大小並將其除以任務的數量(請參閱下面的內容)。
  2. 給每個任務一個字節範圍:[task_number * task_size, task_number * task_size)
  3. 每個任務執行以下操作:
    1. 打開文件(所以每個任務都有自己的文件描述符)
    2. 尋求起始字節位置
    3. 閱讀並丟棄,直到行結束
    4. 讀取線條,放棄它們直到找到一個部分的開始。
    5. Loop:
      1. 解析一段。
      2. 直到下一部分開始的第一行爲止。
      3. 如果起始行中第一個字符的位置在 範圍內,則繼續循環。
    6. 報告結果

這個簡單算法的問題是,它假定一個解析的成本嚴格成正比解析的字符數,而且所有線程將以相同的速度執行。由於這些假設都不可能發生,所以有些線程很可能比其他線程更加完成,然後只是旋轉輪子等待更多的工作。

這可以通過將文件分割成更小的片段並讓每個線程在完成正在處理的片段時獲得下一個可用片段來避免。 (當然,你必須協調工作隊列,但每個工作只有一個同步,這不是很大的開銷。)但是,我不推薦這樣做,因爲輸入文件沒有那麼大可以分成很小的部分。由於需要在實際掃描中找到工作的實際開始和結束,所以存在與每個工作塊相關的一些開銷,並且塊越多,開銷越大。如果塊很小,就會有一些沒有實際的工作。調整參數的正確性需要更多關於工作單元大小的知識,而不是問題所揭示的。

+0

這就是我打算做的。我也在考慮通過'multiprocessing.cpu_count()'進行潛水並分配一個進程來解析每個字節範圍。這會更好嗎?或者python多處理需要太多的開銷,像解析一樣簡單? – ZWiki 2014-09-24 03:54:24

+0

@ZWiki:我認爲我的答案很清楚,python多處理比高效的解析造成更多的開銷。我會回去強調這些話。 – rici 2014-09-24 04:15:34

+0

啊,我錯過了底部的部分評論。是的,主要的問題是這些「部分」不容易分割。例如,即使將它分成8個部分,爲一個過程/線程生成5萬行也不會那麼簡單,因爲這些部分需要着陸在'結構......邊界。我只是不確定一個過程是否會比我現在需要的時間花費更多。不幸的是,這是唯一可以寫入的語言。我不知道我能做到多少效率,其中一部分就是巨大的文件,這就是爲什麼我認爲它分開了它 – ZWiki 2014-09-24 04:32:09