繁体   English   中英

如何通过根据特定列的值分成小块来多处理、多线程处理大文件?

[英]How to multiprocess, multithread a big file by dividing into small chunks based on values of a particular column?

我为生物过程编写了一个 python 程序https://codereview.stackexchange.com/questions/186396/solve-the-phase-state-between-two-haplotype-blocks-using-markov-transition-proba

如果您查看该程序,您会发现该程序一次从两个连续行(或键、值)计算数据需要花费大量时间。 我没有将整个代码放在这里,但为了简单起见,我正在创建一个模拟文件和模拟程序(如下所示),它们在最简单的级别上的行为类似。 在这个模拟程序中,我正在计算len(vals)列并将其写回输出文件。

由于在执行for (k1, v1) and (k2, v2) ....在原始程序(链接上方)中,计算受 CPU/GPU 限制,我想通过- 1)读取整个数据来多处理/线程分析以最快的方式在内存中存储数据2)通过唯一的chr字段将数据分成块3)进行计算4)将其写回文件。 那么,我该怎么做呢?

在给定的模拟文件中,计算太简单了,不能受 GPU/CPU 限制,但我只想知道如果需要,我该怎么做。

注意:我有太多人问我想要实现什么 - 我正在尝试多处理/线程化给定的问题。 如果我把我原来的整个大程序放在这里,没有人会看它。 所以,让我们练习这个小文件和小python程序。

下面是我的代码和数据:

my_data = '''chr\tpos\tidx\tvals
2\t23\t4\tabcd
2\t25\t7\tatg
2\t29\t8\tct
2\t35\t1\txylfz
3\t37\t2\tmnost
3\t39\t3\tpqr
3\t41\t6\trtuv
3\t45\t5\tlfghef
3\t39\t3\tpqr
3\t41\t6\trtu
3\t45\t5\tlfggg
4\t25\t3\tpqrp
4\t32\t6\trtu
4\t38\t5\tlfgh
4\t51\t3\tpqr
4\t57\t6\trtus
'''


def manipulate_lines(vals):
    vals_len = len(vals[3])
    return write_to_file(vals[0:3], vals_len)

def write_to_file(a, b):
    print(a,b)
    to_file = open('write_multiprocessData.txt', 'a')
    to_file.write('\t'.join(['\t'.join(a), str(b), '\n']))
    to_file.close()

def main():
    to_file = open('write_multiprocessData.txt', 'w')
    to_file.write('\t'.join(['chr', 'pos', 'idx', 'vals', '\n']))
    to_file.close()

    data = my_data.rstrip('\n').split('\n')


    for lines in data:
        if lines.startswith('chr'):
            continue
        else:
            lines = lines.split('\t')
        manipulate_lines(lines)


if __name__ == '__main__':
    main()

使用多个进程处理数据时要处理的一个问题是保持顺序。 Python 提出了一种相当不错的处理方法,使用multiprocessing.Pool ,它可用于将进程map到输入数据上。 这将负责按顺序返回结果。

但是,处理可能仍然是乱序的,所以要正确使用它,只需要在子进程中运行处理,而不应该运行 IO 访问。 因此,要在您的情况下使用它,需要对您的代码进行少量重写,所有 IO 操作都发生在主进程中:

from multiprocessing import Pool
from time import sleep
from random import randint

my_data = '''chr\tpos\tidx\tvals
2\t23\t4\tabcd
2\t25\t7\tatg
2\t29\t8\tct
2\t35\t1\txylfz
3\t37\t2\tmnost
3\t39\t3\tpqr
3\t41\t6\trtuv
3\t45\t5\tlfghef
3\t39\t3\tpqr
3\t41\t6\trtu
3\t45\t5\tlfggg
4\t25\t3\tpqrp
4\t32\t6\trtu
4\t38\t5\tlfgh
4\t51\t3\tpqr
4\t57\t6\trtus
'''

def manipulate_lines(vals):
    sleep(randint(0, 2))
    vals_len = len(vals[3])
    return vals[0:3], vals_len

def write_to_file(a, b):
    print(a,b)
    to_file = open('write_multiprocessData.txt', 'a')
    to_file.write('\t'.join(['\t'.join(a), str(b), '\n']))
    to_file.close()

def line_generator(data):
    for line in data:
        if line.startswith('chr'):
            continue
        else:
           yield line.split('\t')

def main():
    p = Pool(5)

    to_file = open('write_multiprocessData.txt', 'w')
    to_file.write('\t'.join(['chr', 'pos', 'idx', 'vals', '\n']))
    to_file.close()

    data = my_data.rstrip('\n').split('\n')

    lines = line_generator(data)
    results = p.map(manipulate_lines, lines)

    for result in results:
        write_to_file(*result)

if __name__ == '__main__':
    main()

该程序不会在其不同的chr值之后拆分列表,而是直接从最多 5 个( Pool参数)子进程中的列表中逐项处理。

为了显示数据仍按预期顺序排列,我向manipulate_lines函数添加了随机睡眠延迟。 这显示了这个概念,但可能没有给出加速的正确视图,因为睡眠进程允许另一个进程并行运行,而计算密集型进程将在其所有运行时间使用 CPU。

可以看出,一旦map调用返回,就必须完成写入文件,这确保所有子进程都已终止并返回其结果。 这种在幕后进行的通信有相当多的开销,因此为了使这一点有益,计算部分必须比写入阶段长得多,并且不能生成太多数据来写入文件。

此外,我还打破了生成器中的for循环。 这样可以根据要求提供multiprocessing.Pool输入。 另一种方法是预处理data列表,然后将该列表直接传递给Pool 不过,我发现生成器解决方案更好,并且峰值内存消耗更小。

另外,关于多线程与多处理的评论; 只要您执行计算量大的操作,就应该使用多处理,至少在理论上,它允许进程在不同的机器上运行。 此外,在 cPython(最常用的 Python 实现)中,线程遇到了另一个问题,即全局解释器锁 (GIL)。这意味着一次只能执行一个线程,因为解释器会阻止所有其他线程的访问。 (有一些例外,例如当使用用 C 编写的模块时,例如 numpy。在这些情况下,可以在执行 numpy 计算时释放 GIL,但通常情况并非如此。)因此,线程主要用于您的程序卡在等待缓慢、无序的 IO。 (插座、端子输入等)

我只使用了几次线程,我还没有测试过下面的这段代码,但是快速浏览一下,for 循环确实是唯一可以从线程中受益的地方。

不过我会让其他人决定的。

import threading

my_data = '''chr\tpos\tidx\tvals
2\t23\t4\tabcd
2\t25\t7\tatg
2\t29\t8\tct
2\t35\t1\txylfz
3\t37\t2\tmnost
3\t39\t3\tpqr
3\t41\t6\trtuv
3\t45\t5\tlfghef
3\t39\t3\tpqr
3\t41\t6\trtu
3\t45\t5\tlfggg
4\t25\t3\tpqrp
4\t32\t6\trtu
4\t38\t5\tlfgh
4\t51\t3\tpqr
4\t57\t6\trtus
'''


def manipulate_lines(vals):
    vals_len = len(vals[3])
    return write_to_file(vals[0:3], vals_len)

def write_to_file(a, b):
    print(a,b)
    to_file = open('write_multiprocessData.txt', 'a')
    to_file.write('\t'.join(['\t'.join(a), str(b), '\n']))
    to_file.close()

def main():
    to_file = open('write_multiprocessData.txt', 'w')
    to_file.write('\t'.join(['chr', 'pos', 'idx', 'vals', '\n']))
    to_file.close()

    data = my_data.rstrip('\n').split('\n')

    for lines in data:
        if not lines.startswith('chr'):
            lines = lines.split('\t')
        threading.Thread(target = manipulate_lines, args = (lines)).start()


if __name__ == '__main__':
    main()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM