Python 多处理池比手动实例化多个进程慢得多
我从一个大文件中读取一个块,将其作为行列表加载到内存中,然后在每一行上处理一个任务 顺序解决方案花费的时间太长,所以我开始考虑如何将其并行化 我提出的第一个解决方案是进程和管理列表中每个子进程的部分Python 多处理池比手动实例化多个进程慢得多,python,performance,multiprocessing,python-multiprocessing,pool,Python,Performance,Multiprocessing,Python Multiprocessing,Pool,我从一个大文件中读取一个块,将其作为行列表加载到内存中,然后在每一行上处理一个任务 顺序解决方案花费的时间太长,所以我开始考虑如何将其并行化 我提出的第一个解决方案是进程和管理列表中每个子进程的部分 import multiprocessing as mp BIG_FILE_PATH = 'big_file.txt' CHUNKSIZE = '1000000' N_PROCESSES = mp.cpu_count() def read_in_chunks(file_object, chun
import multiprocessing as mp
BIG_FILE_PATH = 'big_file.txt'
CHUNKSIZE = '1000000'
N_PROCESSES = mp.cpu_count()
def read_in_chunks(file_object, chunk_size=1024):
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
with open(BIG_FILE_PATH, encoding="Latin-1") as file:
for piece in read_in_chunks(file, CHUNKSIZE):
jobs = []
piece_list = piece.splitlines()
piece_list_len = len(piece_list)
item_delta = round(piece_list_len/N_PROCESSES)
start = 0
for process in range(N_PROCESSES):
finish = start + item_delta
p = mp.Process(target=work, args=(piece_list[start:finish]))
start = finish
jobs.append(p)
p.start()
for job in jobs:
job.join()
它大约在2498毫秒内完成每个块
然后我发现了自动管理切片的Pool工具
import multiprocessing as mp
BIG_FILE_PATH = 'big_file.txt'
CHUNKSIZE = '1000000'
N_PROCESSES = mp.cpu_count()
def read_in_chunks(file_object, chunk_size=1024):
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
with open(BIG_FILE_PATH, encoding="Latin-1") as file:
with mp.Pool(N_PROCESSES) as pool:
for piece in read_in_chunks(file, CHUNKSIZE):
piece_list = piece.splitlines()
pool.map(work, piece_list)
它在大约15540ms的时间内完成每个数据块,速度是手动的6倍,但仍然比顺序的快
我用错泳池了吗?
有没有更好或更快的方法来做到这一点
谢谢你的阅读
更新
正如汉努所说,游泳池的开销相当大
Process方法调用的work函数需要一个行列表
由于池决定切片的方式,池方法调用的work函数需要一行
import multiprocessing as mp
BIG_FILE_PATH = 'big_file.txt'
CHUNKSIZE = '1000000'
N_PROCESSES = mp.cpu_count()
def read_in_chunks(file_object, chunk_size=1024):
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
with open(BIG_FILE_PATH, encoding="Latin-1") as file:
with mp.Pool(N_PROCESSES) as pool:
for piece in read_in_chunks(file, CHUNKSIZE):
piece_list = piece.splitlines()
pool.map(work, piece_list)
我不太清楚如何让游泳池一次给某个工人不止一条线
这应该能解决问题吗
更新2
最后一个问题,还有第三种更好的方法吗?我不知道这是否可行,但你可以试试吗
if __name__ == "__main__":
with open(BIG_FILE_PATH, encoding="Latin-1") as file:
with mp.Pool(N_PROCESSES) as pool:
for piece in read_in_chunks(file, CHUNKSIZE):
piece_list = piece.splitlines()
pool.map(work, piece_list)
我的理由是:1。pool.map(),只需一次,您的代码就会循环它
二,。我猜这个循环会让它变慢
三,。因为并行处理应该更快,呵呵我不能完全确定这一点,但在我看来,您的程序在提交给工人的内容上有很大的不同 在Process方法中,您似乎提交了大量行:
p = mp.Process(target=work, args=(piece_list[start:finish]))
但当您使用Pool时,您会执行以下操作:
for piece in read_in_chunks(file, CHUNKSIZE):
piece_list = piece.splitlines()
pool.map(work, piece_list)
您将文件分块读取,但当您使用分割线时,片段列表
iterable提交一个单位
这意味着在流程方法中,您提交的子任务数量与CPU数量相同,但在池方法中,您提交的任务数量与源数据的行数相同。如果您有很多行,这将在池中产生巨大的编排开销,因为每个工作者一次只处理一行,然后完成,返回结果,然后池将另一行提交给新释放的工作者
如果这就是这里发生的事情,它肯定解释了为什么池需要更长的时间才能完成
如果将读卡器用作iterable并跳过行拆分部分,会发生什么情况:
pool.map(work, read_in_chunks(file, CHUNKSIZE))
哦,孩子!这是一个相当复杂的过程,但仍然很有趣
Pool.map从迭代器中获取、酸洗并将每个项分别传递给每个工人。工人完成后,冲洗并重复,获得->酸洗->通过。这造成了明显的间接成本
这实际上是因为Pool.map不够聪明,无法知道迭代器的长度,也无法有效地创建列表列表并将其中的每个列表(块)传递给工作程序
但是,这是可以帮助的。
简单地将列表转换为具有列表理解功能的块(列表)列表就像一个符咒,并将开销降低到与处理方法相同的水平
import multiprocessing as mp
BIG_FILE_PATH = 'big_file.txt'
CHUNKSIZE = '1000000'
N_PROCESSES = mp.cpu_count()
def read_in_chunks(file_object, chunk_size=1024):
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
with open(BIG_FILE_PATH, encoding="Latin-1") as file:
with mp.Pool(N_PROCESSES) as pool:
for piece in read_in_chunks(file, CHUNKSIZE):
piece_list = piece.splitlines()
piece_list_len = len(piece_list)
item_delta = round(piece_list_len / N_PROCESSES)
pool.map(work, [piece_list[i:i + item_delta] for i in range(0, piece_list_len, item_delta)])
这个包含列表迭代器列表的池的运行时间与Process方法完全相同。您正在循环中创建池。因此,它被一次又一次地创造出来。在开始循环之前创建一次,如图所示。哦,不,我怎么能看不到呢!谢谢,但是运行时间没有改变。如果我按照你的建议使用读卡器,内存会饱和并开始交换,直到速度严重下降。但我确实明白你关于开销的观点。事实上,processmethodwork函数需要一个行列表。由于池是如何迭代块的,所以pool method work函数只需要一行。然后我建议降低CHUNKSIZE。您的read_in_chunks是一个迭代器,所以将其与map一起使用应该是绝对好的。它不会读取内存中的文件,因此内存问题是由您的工作人员的工作板上有太多工作人员或工作人员太多造成的。试着减少一块或更少的工人,看看会发生什么。这通常是一个尝试和错误的尝试,以找到最佳点。但池中并没有任何东西规定您的工人应该只处理一行。您应该能够使用与Process相同的worker,并提交块而不是行。我同意您的看法。但不管CHUNKSIZE如何,我相信在EOF之前,池都是从迭代器读取的。它从不停止阅读并开始工作。您正在使用超出其范围的可变工件列表。我在整个文件的子集块上使用map,因为文件太大,无法在内存中完全加载。
import multiprocessing as mp
BIG_FILE_PATH = 'big_file.txt'
CHUNKSIZE = '1000000'
N_PROCESSES = mp.cpu_count()
def read_in_chunks(file_object, chunk_size=1024):
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
with open(BIG_FILE_PATH, encoding="Latin-1") as file:
with mp.Pool(N_PROCESSES) as pool:
for piece in read_in_chunks(file, CHUNKSIZE):
piece_list = piece.splitlines()
piece_list_len = len(piece_list)
item_delta = round(piece_list_len / N_PROCESSES)
pool.map(work, [piece_list[i:i + item_delta] for i in range(0, piece_list_len, item_delta)])