如何在python代码段上应用并行或异步I/O文件编写
首先,我们得到了以下代码:如何在python代码段上应用并行或异步I/O文件编写,python,asynchronous,io,Python,Asynchronous,Io,首先,我们得到了以下代码: from validate_email import validate_email import time import os def verify_emails(email_path, good_filepath, bad_filepath): good_emails = open(good_filepath, 'w+') bad_emails = open(bad_filepath, 'w+') emails = set()
from validate_email import validate_email
import time
import os
def verify_emails(email_path, good_filepath, bad_filepath):
good_emails = open(good_filepath, 'w+')
bad_emails = open(bad_filepath, 'w+')
emails = set()
with open(email_path) as f:
for email in f:
email = email.strip()
if email in emails:
continue
emails.add(email)
if validate_email(email, verify=True):
good_emails.write(email + '\n')
else:
bad_emails.write(email + '\n')
if __name__ == "__main__":
os.system('cls')
verify_emails("emails.txt", "good_emails.txt", "bad_emails.txt")
当emails.txt
包含大量行(>1k)时,我希望联系SMTP服务器是我程序中最昂贵的部分。使用某种形式的并行或异步I/O应该会大大加快速度,因为我可以等待多个服务器响应,而不是顺序等待
据我所知:
异步I/O通过将I/O请求排队到文件进行操作
描述符,独立于调用进程进行跟踪。存档
支持异步I/O(原始磁盘设备)的描述符
通常情况下,进程可以调用aio_read()(例如)来请求
从文件描述符读取的字节数。系统调用
立即返回,无论I/O是否已完成。一些时间
稍后,该过程会轮询操作系统以确定是否完成
I/O(也就是说,缓冲区中充满了数据)
老实说,我不太明白如何在我的程序上实现异步I/O。谁能花点时间给我解释一下整个过程吗
根据PArakleta建议编辑:
from validate_email import validate_email
import time
import os
from multiprocessing import Pool
import itertools
def validate_map(e):
return (validate_email(e.strip(), verify=True), e)
seen_emails = set()
def unique(e):
if e in seen_emails:
return False
seen_emails.add(e)
return True
def verify_emails(email_path, good_filepath, bad_filepath):
good_emails = open(good_filepath, 'w+')
bad_emails = open(bad_filepath, 'w+')
with open(email_path, "r") as f:
for result in Pool().imap_unordered(validate_map,
itertools.ifilter(unique, f):
(good, email) = result
if good:
good_emails.write(email)
else:
bad_emails.write(email)
good_emails.close()
bad_emails.close()
if __name__ == "__main__":
os.system('cls')
verify_emails("emails.txt", "good_emails.txt", "bad_emails.txt")
你问错问题了
查看了validate\u电子邮件包后,您真正的问题是没有有效地批量处理结果。您应该只在每个域中执行一次MX查找,然后只连接到每个MX服务器一次,通过握手,然后在单个批中检查该服务器的所有地址。谢天谢地,validate\u email
包为您提供了MX结果缓存,但您仍然需要按服务器对电子邮件地址进行分组,以便将查询批处理到服务器本身
您需要编辑validate\u email
包来实现批处理,然后可能使用实际的线程
库而不是多处理
为每个域提供一个线程
如果你的程序运行缓慢,那么一定要对它进行分析,找出它实际花费的时间,而不是盲目地尝试应用优化技巧
请求的解决方案
如果您使用的是缓冲IO,并且您的用例适合OS缓冲,则IO已经是异步的。您唯一可能获得一些优势的地方是预读,但是如果您使用迭代器访问文件(您正在这样做),Python已经做到了这一点。异步IO对于移动大量数据并已禁用操作系统缓冲区以防止复制数据两次的程序来说是一个优势
您需要对您的程序进行实际的评测/基准测试,看看它是否有改进的余地。如果您的磁盘尚未达到吞吐量限制,则有机会通过并行执行对每个电子邮件(地址?)的处理来提高性能。检查这一点的最简单方法可能是检查运行程序的内核是否已达到最大值(即,您是CPU绑定的,而不是IO绑定的)
如果您是CPU受限的,那么您需要查看线程。不幸的是,Python线程不能并行工作,除非您有非Python的工作要做,否则您必须使用(我假设validate\u email
是Python函数)
如何准确地进行取决于程序中的瓶颈在哪里,以及达到IO绑定点所需的速度(因为实际速度不能超过这个速度,当达到该点时,可以停止优化)
emails
set对象很难共享,因为您需要锁定它,所以最好将其保存在一个线程中。查看库,最容易使用的机制可能是
使用此方法,您需要将文件iterable包装在一个丢弃重复项的文件中,然后将其馈送到一个,然后迭代该结果并写入两个输出文件
比如:
with open(email_path) as f:
for result in Pool().imap_unordered(validate_map,
itertools.ifilter(unique, f):
(good, email) = result
if good:
good_emails.write(email)
else:
bad_emails.write(email)
seen_emails = set()
def unique(e):
if e in seen_emails:
return False
seen_emails.add(e)
return True
validate\u map
函数应该很简单,如:
def validate_map(e):
return (validate_email(e.strip(), verify=True), e)
unique
函数应该类似于:
with open(email_path) as f:
for result in Pool().imap_unordered(validate_map,
itertools.ifilter(unique, f):
(good, email) = result
if good:
good_emails.write(email)
else:
bad_emails.write(email)
seen_emails = set()
def unique(e):
if e in seen_emails:
return False
seen_emails.add(e)
return True
ETA:我刚刚意识到validate\u email
是一个实际上与SMTP服务器联系的库。鉴于Python代码中并不繁忙,您可以使用线程。虽然API不如多处理库方便,但您可以使用它来创建基于线程的池
如果您受到CPU的限制,那么拥有比内核更多的线程/进程就不值得了,但由于您的瓶颈是网络IO,您可以从更多的线程/进程中获益。由于进程很昂贵,您希望交换到线程,然后增加并行运行的数量(尽管您应该礼貌地避免DOS攻击您连接的服务器)
考虑从多处理中导入。虚拟导入池作为线程池
,然后调用线程池(进程=32)。imap_unordered()
不是一个真正的答案,但您是否考虑过类似map reduce的机制?@Brunodesshuilliers不,我没有。这会比我要求的更好吗?你可以在回答中详细阐述你的评论,不知道它是否“会更好”,但至少你不必处理异步io。。。这里的想法是:1。将源文件拆分为块2。将每个区块发送到子流程3。每个子进程写入自己的结果文件(在主进程传入的位置上)4。完成每个子流程后,主进程将连接结果文件。这是map reduce的一个非常粗糙和简单的版本,但它应该很容易实现,甚至可以工作。它听起来像是一个接近线程的解决方案,但听起来不是很轻量级,因为:1-如果我有一个包含200k电子邮件的文件,会发生什么?我需要创建多少个文件/块?(即使是1k也能持续30分钟以上)。2-我必须找到行数,才能知道要拆分原始数字的块数