[英]How to process 100 Million + text lines at once
我有这段代码可以逐行读取和处理文本文件,问题是我的文本文件有 15 到 20 亿行,而且要花很长时间。 是否可以同时处理超过 1 亿行代码?
from cryptotools.BTC.HD import check, WORDS
with open("input.txt", "r") as a_file:
for line in a_file:
stripped_line = line.strip()
for word in WORDS:
mnemonic = stripped_line.format(x=word)
if check(mnemonic):
print(mnemonic)
with open("print.txt", "a") as i:
i.write(mnemonic)
i.write("\n")
输入文件具有以下示例行:
gloom document {x} stomach uncover peasant sock minor decide special roast rural
happy seven {x} gown rally tennis yard patrol confirm actress pledge luggage
tattoo time {x} other horn motor symbol dice update outer fiction sign
govern wire {x} pill valid matter tomato scheme girl garbage action pulp
要一次处理 1 亿行,您必须有 1 亿个线程。 另一种提高代码速度的方法是将工作分配给不同的线程(少于 1 亿个)。
因为文件的写入和读取操作不是异步的,所以最好在程序开始时读取所有文件并在最后写出处理过的数据。 在下面的代码中,我假设您不关心写文件的顺序。 但是,如果顺序很重要,您可以设置一个字典,该字典以特定线程详细说明的当前行的位置值作为键,并在最后进行相应排序。
import concurrent.futures as cf
N_THREADS = 20
result = []
def doWork(data):
for line in data:
#do what you have to do
result.append(mnemonic)
m_input = open("input.txt", "r")
lines = [line for line in m_input]
#the data for the threads will be here
#as a list of rows for each thread
m_data= { i: [] for i in range(0, N_THREADS)}
for l, n in zip(lines, range(0, len(lines))):
m_data[n%N_THREADS].append(l)
'''
If you have to trim the number of threads uncomment these lines
m_data= { k:v for k, v in m_data.items() if len(v) != 0}
N_THREADS = N_THREADS if len(m_data) > N_THREADS else len(m_data)
if(N_THREADS == 0):
exit()
'''
with cf.ThreadPoolExecutor(max_workers=N_THREADS) as tp:
for d in m_data.keys():
tp.submit(doWork,m_data[d])
#work done
output = open("print.txt", "w")
for item in result:
output.write(f"{item}\n")
output.close()
更改您认为最有效的线程数。
上面的代码虽然非常快,但使用了大量的 memory,因为在 memory 中加载了整个文件,然后对其进行处理。
然后你有两个选择:
下面的代码从文件中读取一行(1000 万行,大约 500 MB),然后将该数据发送到管理固定线程数的 class。 目前我每次完成都会产生一个新线程,实际上可以更有效,始终使用相同的线程并为每个线程使用一个队列。 然后我生成一个编写writer
线程,它唯一的工作是写入将包含结果的out.txt
文件。 在我的测试中,我只读取文本文件并在另一个文件中写入相同的行。
我发现的是以下内容(使用 1000 万行文件):
两个程序使用相同数量的线程获得定时结果。
从这些结果可以明显看出,memory 优化代码在使用更少内存的情况下运行速度明显变慢。 您可以调整内部参数,例如线程数或最大队列大小以提高性能,请记住这会影响 memory 的使用。 经过大量测试后,我建议将文件拆分为多个子文件,以适合您的 memory 并运行代码的原始版本(见上文),因为在我看来,时间和速度之间的权衡根本不合理。
在这里我放了我为 memory cunsumption 优化的代码,但请记住,就线程管理而言,它没有以任何重要的方式进行优化,一个建议是始终使用相同的线程并使用多个队列将数据传递给这些线程.
在这里,我留下了我用来优化 memory 消耗的代码(是的,它比 XD 上面的代码复杂得多,而且可能比它需要的要复杂得多):
from threading import Thread
import time
import os
import queue
MAX_Q_SIZE = 100000
m_queue = queue.Queue(maxsize=MAX_Q_SIZE)
end_thread = object()
def doWork(data):
#do your work here, before
#checking if the queue is full,
#otherwise when you finish the
#queue might be full again
while m_queue.full():
time.sleep(0.1)
pass
m_queue.put(data)
def writer():
#check if file exists or creates it
try:
out = open("out.txt", "r")
out.close()
except FileNotFoundError:
out = open("out.txt", "w")
out.close()
out = open("out.txt", "w")
_end = False
while True:
if m_queue.qsize == 0:
if _end:
break
continue
try:
item = m_queue.get()
if item is end_thread:
out.close()
_end = True
break
global written_lines
written_lines += 1
out.write(item)
except:
break
class Spawner:
def __init__(self, max_threads):
self.max_threads = max_threads
self.current_threads = [None]*max_threads
self.active_threads = 0
self.writer = Thread(target=writer)
self.writer.start()
def sendWork(self, data):
m_thread = Thread(target=doWork, args=(data, ))
replace_at = -1
if self.active_threads >= self.max_threads:
#wait for at least 1 thread to finish
while True:
for index in range(self.max_threads):
if self.current_threads[index].is_alive() :
pass
else:
self.current_threads[index] = None
self.active_threads -= 1
replace_at = index
break
if replace_at != -1:
break
#else: no threads have finished, keep waiting
if replace_at == -1:
#only if len(current_threads) < max_threads
for i in range(len(self.current_threads)):
if self.current_threads[i] == None:
replace_at = i
break
self.current_threads[replace_at] = m_thread
self.active_threads += 1
m_thread.start()
def waitEnd(self):
for t in self.current_threads:
if t.is_alive():
t.join()
self.active_threads -= 1
while True:
if m_queue.qsize == MAX_Q_SIZE:
time.sleep(0.1)
continue
m_queue.put(end_thread)
break
if self.writer.is_alive():
self.writer.join()
start_time = time.time()
spawner = Spawner(50)
with open("input.txt", "r") as infile:
for line in infile:
spawner.sendWork(line)
spawner.waitEnd()
print("--- %s seconds ---" % (time.time() - start_time))
您可以暂时删除这些打印件,我留下这些只是为了参考,以了解我如何计算程序运行所花费的时间,以及在下面您可以从任务管理器中找到这两个程序的执行屏幕截图。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.