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



好吧,这个文件是410k行代码。现在我在1.4秒内解析它,但我需要它更快。不过,这个文件有一些奇怪的地方。。。

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

有没有一种方法可以使用线程将任务拆分为多个线程,将数据添加到同一映射中?

附言:我不是在找人帮我做这件事,我只是提供了一个文件结构的例子,这样你就知道我不能只处理每一行,而是根据结构从[start:finion]开始处理。

根据请求,我正在分析的示例:

; 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 FilesDS-5bin..includestdio.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

您最好优化解析器代码,或者用不同的语言编写。

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

如果您的单个线程在解析过程中不需要有关结构的全局信息,那么它们可以各自创建自己的字典,然后您可以在最后合并所有字典。将(可拾取的)Python对象从一个进程发送到另一个进程是很容易的,但要考虑以下几点:您的任务是解析文本表示并创建内部表示。拾取和取消拾取对象包括获取一个内部表示,从中生成一个字符串,然后在通信通道的另一端解析该字符串。换句话说,您的解析任务只是生成另一个解析任务,并为序列化带来一些额外的开销。除了unpickler可能是一个比您编写的更快的解析器之外,这不太可能是一场胜利。这让我们回到优化解析器的问题上来。

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

  1. 找到文件的大小,并将其除以任务数(见下文)
  2. 给每个任务一个字节范围:[task_number * task_size, task_number * task_size)
  3. 每个任务执行以下操作:
    1. 打开文件(这样每个任务都有自己的文件描述符)
    2. 查找到起始字节位置
    3. 读取并丢弃,直到行结束
    4. 读取行,丢弃它们,直到找到节的开头
    5. 循环:
      1. 分析一节
      2. 读到下一节开头的第一行
      3. 如果起始行中第一个字符的位置在范围,继续循环
    6. 报告结果

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

这可以通过将文件拆分成更小的部分来避免,并让每个线程在完成正在处理的工作时获得下一个可用部分,我在上面不建议这样做,因为输入文件并不是很大,所以它可以分成很小的部分。由于工作的实际开始和结束需要通过实际扫描来找到,因此每个工作块都会有一些开销,并且块越多,开销就越多。如果区块足够小,就会有一些根本没有实际工作。要获得正确的调整参数,需要比问题所揭示的更多关于工作单元大小的知识。

最新更新