如何使用线程来帮助解析大文件

时间:2014-09-23 21:41:40

标签: python multithreading parsing

好吧,所以这个文件是410k行代码。现在我在1.4秒内解析它,但我需要它更快。虽然这个文件有几个奇怪的东西......

该文件的结构类似于此(感谢ARM):ARM fromelf
基本上我将所有这些解析为一个映射,其中键是结构的名称,在这种情况下,由于ARM生成警告,可以复制。这种情况下的值是后面的字段。

有没有办法可以使用线程将任务拆分为多个线程,将数据添加到同一个地图中?

P.S。我没有找人为我这样做,我只提供了一个文件结构是什么样的例子,所以你理解我不能只处理每一行,而是根据结构从[start:finish]开始处理。 / p>

每个请求,我正在解析的一个示例:

; 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

1 个答案:

答案 0 :(得分:0)

最好优化解析器代码,或用其他语言编写代码。

在标准Python实现(“CPython”)中,有效多进程的唯一方法是使用multiprocessing模块,该模块依赖于使用多个unix进程而不是线程(线程实际上不可能进行线程化)由于全局解释器锁定的绑定任务)。您可以使用共享内存对象甚至共享字典(请参阅Managers),但基本上进程间通信非常昂贵,并且很快就会消耗多任务处理的优势。

如果您的个别线程在解析期间不需要有关结构的全局信息,则每个线程都可以创建自己的字典,然后您可以在最后合并所有字典。将(可选择的)Python对象从一个进程发送到另一个进程很容易,但请考虑以下内容:您的任务是解析文本表示并创建内部表示。对对象进行pickle和unpickling包括获取内部表示,从中生成字符串,然后在通信通道的另一端解析字符串。换句话说,您的解析任务只是生成另一个解析任务,还有一些额外的序列化开销。这不太可能是一个胜利,除了unpickler可能是一个比你写的更快的解析器。这让我们回到优化解析器。

并行化问题的一部分通常是直接的,即在进程之间拆分任务。假设要解析的块(start:finish)不是太大 - 也就是说,你的410k行包含数千个这样的子任务 - 那么就有一个简单的策略:

  1. 找到文件的大小,并将其除以任务数量(见下文)。
  2. 为每个任务提供一个字节范围:[task_number * task_size, task_number * task_size)
  3. 每项任务执行以下操作:
    1. 打开文件(因此每个任务都有自己的文件描述符)
    2. 寻找起始字节位置
    3. 阅读并放弃直到行尾
    4. 读取行,将其丢弃,直到找到某个部分的开头。
    5. 循环:
      1. 解剖一段。
      2. 阅读下一部分开头的第一行。
      3. 如果起始行中第一个字符的位置在 范围,继续循环。
    6. 报告结果

  4. 这个简单算法的问题在于它假设解析的成本与解析的字符数严格成比例,并且所有线程将以相同的速度执行。由于这些假设都不可能,因此很有可能某些线程会先于其他线程完成,然后只需旋转轮子等待更多工作。

    这可以通过将文件拆分成更小的部分并让每个线程在完成正在处理的文件时获得下一个可用的部分来避免。 (当然,你必须协调工作队列,但每个工作块只有一个同步,这不是很多开销。)但是,我没有推荐这个,因为输入文件不是那么大,以至于它可以分成很小的一块。由于需要在实际扫描中找到实际的工作开始和结束,因此与每个工作块相关的一些开销并且存在的块越多,开销就越大。如果块足够小,将会有一些根本没有实际工作。正确地获得调整参数需要更多关于工作单元大小的知识,而不是问题所揭示的。