Python中的MPI:同时从一个文件加载数据

时间:2016-03-31 23:20:05

标签: python concurrency io parallel-processing mpi

我是python以及MPI的新手。 我有一个巨大的数据文件,10Gb,我想加载它,即列表或更高效的,请建议。

这是我将文件内容加载到列表中的方式

def load(source, size):
     data = [[] for _ in range(size)]
          ln = 0
          with open(source, 'r') as input:
                  for line in input:
                          ln += 1
                          data[ln%size].sanitize(line)
          return data

注意:

  • 来源:是文件名
  • size:是并发进程的数量,我将数据划分为子列表的[size]。

用于在python中使用MPI进行并行计算。

请告知如何更有效,更快地加载数据。我搜索了几天,但我无法获得符合我目的的任何结果,如果存在,请在此处注明链接。

此致

2 个答案:

答案 0 :(得分:0)

如果我理解了这个问题,那么你的瓶颈就不是Python数据结构。正是I / O速度限制了程序的效率。 如果文件是在H.D.D中的连续块中写入的,那么我不知道比从第一个字节开始到结尾读取文件更快地读取它的方法。 但是如果文件是碎片的,则创建多个线程,每个线程读取文件的一部分。必须减慢读取过程,但现代HDD实现了一种名为NCQ(本机命令队列)的技术。它的工作原理是对地址接近HDD头当前位置的扇区的读操作给予高度优先。因此,使用多个线程提高了读取操作的整体速度。

要为您的程序提供Python中的高效数据结构,您需要提及您将对数据执行哪些操作? (删除,添加,插入,搜索,追加等)以及频率如何?

顺便说一句,如果你使用商用硬件,10GB的RAM是昂贵的。尝试通过加载必要的计算数据,然后用新数据替换结果来进行下一次操作,从而减少对RAM数量的需求。您可以将计算与I / O操作重叠,以提高性能。

答案 1 :(得分:0)

(原创)使用酸洗的解决方案

您的任务策略可以这样:

  • 将大文件拆分为较小的文件,确保它们在行边界上划分
  • 拥有Python代码,可以将较小的文件转换为结果记录列表并将其另存为 腌制文件
  • 并行运行所有较小文件的python代码(使用Python或其他方法)
  • 运行集成代码,逐个获取pickle文件,从中加载列表并附加它 到最后的结果。

要获得任何收益,您必须小心,因为开销可以克服并行所有可能的收益 运行:

  • 因为Python使用全局解释器锁(GIL),所以不要使用线程进行并行处理 流程。由于流程不能简单地传递数据,你必须腌制它们并让另一个流淌 (最终整合)部分从中读取结果。
  • 尽量减少循环次数。因此,最好是:
    • 不要将大文件拆分为太多较小的部分。要使用核心的力量,最合适 核心数量的部件数量(或者可能是核心数量的两倍,但是会变得更高 花太多时间在流程之间进行切换)。
    • 酸洗允许保存特定物品,但最好创建物品(记录)和泡菜列表 列表作为一个项目。酸洗1000个物品的清单将比酸洗的1000倍快 小件物品一个接一个。
  • 一些任务(拆分文件,并行调用转换任务)通常可以更快完成 通过系统中的现有工具。如果您有此选项,请使用该选项。

在我的小测试中,我创建了一个包含10万行内容" 98-BBBBBBBBBBBBBB"的文件, " 99-BBBBBBBBBBB"测试将其转换为数字列表[....,98,99,...]。

对于拆分,我使用了Linux命令split,要求创建4个保留行边界的部分:

$ split -n l/4 long.txt

这会创建较小的文件xaaxabxacxad

要转换我在后面脚本中使用的每个较小的文件,将内容转换为文件 扩展名.pickle并包含腌制列表。

# chunk2pickle.py 
import pickle
import sys


def process_line(line):
    return int(line.split("-", 1)[0])


def main(fname, pick_fname):
    with open(pick_fname, "wb") as fo:
        with open(fname) as f:
            pickle.dump([process_line(line) for line in f], fo)


if __name__ == "__main__":
    fname = sys.argv[1]
    pick_fname = fname + ".pickled"
    main(fname, pick_fname)

将一大块行转换为腌制记录列表:

$ python chunk2pickle xaa

并创建文件xaa.pickled

但是,由于我们需要并行执行此操作,因此我使用了parallel工具(必须安装到其中) 系统):

$ parallel -j 4 python chunk2pickle.py {} ::: xaa xab xac xad

我在磁盘上找到了扩展名为.pickled的新文件。

-j 4要求并行运行4个进程,将其调整到您的系统或将其保留,并将其保留 默认为您拥有的核心数。

parallel也可以通过ls等其他方式获取参数列表(在我们的例子中输入文件名) 命令:

$ ls x?? |parallel -j 4 python chunk2pickle.py {}

要整合结果,请使用脚本integrate.py

# integrate.py
import pickle


def main(file_names):
    res = []
    for fname in file_names:
        with open(fname, "rb") as f:
            res.extend(pickle.load(f))
    return res

if __name__ == "__main__":
    file_names = ["xaa.pickled", "xab.pickled", "xac.pickled", "xad.pickled"]
    # here you have the list of records you asked for
    records = main(file_names)
    print records

在我的回答中,我使用了几个外部工具(splitparallel)。你可能会做类似的任务 用Python也是。我的回答是只关注为您提供保留Python代码的选项 将行转换为所需的数据结构。完整的纯Python答案不在这里(它 会变得更长,也可能更慢。

使用流程池的解决方案(无需明确的酸洗)

以下解决方案使用Python的多处理。在这种情况下,不需要腌制结果 显式(我不确定,如果它是由库自动完成的,或者它是没有必要的 数据通过其他方式传递。)

# direct_integrate.py
from multiprocessing import Pool


def process_line(line):
    return int(line.split("-", 1)[0])


def process_chunkfile(fname):
    with open(fname) as f:
        return [process_line(line) for line in f]


def main(file_names, cores=4):

    p = Pool(cores)
    return p.map(process_chunkfile, file_names)


if __name__ == "__main__":
    file_names = ["xaa", "xab", "xac", "xad"]
    # here you have the list of records you asked for
    # warning: records are in groups.
    record_groups = main(file_names)
    for rec_group in record_groups:
        print(rec_group)

此更新的解决方案仍然假设,大文件以四个较小的文件形式提供。