Python 使用芹菜处理巨大的文本文件 背景
我正在研究使用芹菜(3.1.8)来处理巨大的文本文件(约30GB)。这些文件的格式为,包含约118M个测序“读取”,基本上每个都是头、DNA序列和质量字符串的组合。此外,这些序列来自成对的末端序列运行,因此我同时迭代两个文件(通过itertools.izip)。我希望能够做的是获取每一对读取,将它们发送到队列,并在集群中的一台机器上处理它们(不管是哪台机器),以便在需要进行清理(例如,基于质量)的情况下返回已清理的读取版本 我已经建立了芹菜和rabbitmq,我的员工如下:Python 使用芹菜处理巨大的文本文件 背景,python,performance,rabbitmq,celery,hpc,Python,Performance,Rabbitmq,Celery,Hpc,我正在研究使用芹菜(3.1.8)来处理巨大的文本文件(约30GB)。这些文件的格式为,包含约118M个测序“读取”,基本上每个都是头、DNA序列和质量字符串的组合。此外,这些序列来自成对的末端序列运行,因此我同时迭代两个文件(通过itertools.izip)。我希望能够做的是获取每一对读取,将它们发送到队列,并在集群中的一台机器上处理它们(不管是哪台机器),以便在需要进行清理(例如,基于质量)的情况下返回已清理的读取版本 我已经建立了芹菜和rabbitmq,我的员工如下: celery wor
celery worker -A tasks --autoreload -Q transient
配置如下:
from kombu import Queue
BROKER_URL = 'amqp://guest@godel97'
CELERY_RESULT_BACKEND = 'rpc'
CELERY_TASK_SERIALIZER = 'pickle'
CELERY_RESULT_SERIALIZER = 'pickle'
CELERY_ACCEPT_CONTENT=['pickle', 'json']
CELERY_TIMEZONE = 'America/New York'
CELERY_ENABLE_UTC = True
CELERYD_PREFETCH_MULTIPLIER = 500
CELERY_QUEUES = (
Queue('celery', routing_key='celery'),
Queue('transient', routing_key='transient',delivery_mode=1),
)
我选择了使用rpc后端和pickle序列化来提高性能,也选择了不使用
将任何内容写入“暂时”队列中的磁盘(通过传递模式)
芹菜创业
为了设置芹菜框架,我首先在64路机箱上启动rabbitmq服务器(3.2.3,Erlang R16B03-1),将日志文件写入fast/tmp磁盘。工作进程(如上所述)在集群上的每个节点上启动(大约34个),范围从8路到64路SMP,总共688个核心。因此,我有大量可用的CPU供工作人员用于处理队列
工作提交/绩效
芹菜启动并运行后,我通过ipython笔记本提交作业,如下所示:
files = [foo, bar]
f1 = open(files[0])
f2 = open(files[1])
res = []
count = 0
for r1, r2 in izip(FastqGeneralIterator(f1), FastqGeneralIterator(f2)):
count += 1
res.append(tasks.process_read_pair.s(r1, r2))
if count == 10000:
break
t.stop()
g = group(res)
for task in g.tasks:
task.set(queue="transient")
result = g.delay()
res.append(tasks.speed.apply_async(args=("FML",), queue="transient"))
这大约需要1.5秒来读取10000对数据。然后,我给delay打电话,要求小组向工人们提交,大约需要20秒,如下所示:
files = [foo, bar]
f1 = open(files[0])
f2 = open(files[1])
res = []
count = 0
for r1, r2 in izip(FastqGeneralIterator(f1), FastqGeneralIterator(f2)):
count += 1
res.append(tasks.process_read_pair.s(r1, r2))
if count == 10000:
break
t.stop()
g = group(res)
for task in g.tasks:
task.set(queue="transient")
result = g.delay()
res.append(tasks.speed.apply_async(args=("FML",), queue="transient"))
通过rabbitmq控制台进行监控,我发现我做得还不错,但速度还不够快
问题:
那么,有没有办法加快速度?我的意思是,我希望看到每秒处理至少50000个读对,而不是500个。我的芹菜配置中有什么明显的缺失吗?我的工人和兔子日志基本上是空的。我想知道如何提高我的表现。每个读对的处理速度也相当快:
[2014-01-29 13:13:06,352: INFO/Worker-1] tasks.process_read_pair[95ec7f2f-0143-455a-a23b-c032998951b8]: HWI-ST425:143:C04A5ACXX:3:1101:13938:2894 1:N:0:ACAGTG HWI-ST425:143:C04A5ACXX:3:1101:13938:2894 2:N:0:ACAGTG 0.00840497016907 sec
到目前为止
到目前为止,我已经用谷歌搜索了芹菜、性能、路由、rabbitmq等我能想到的一切。我已经浏览了芹菜网站和文档。如果我不能获得更高的性能,我将不得不放弃这种方法,转而采用另一种解决方案(基本上是将工作划分为许多较小的物理文件,并直接在每个计算节点上使用多处理或其他方法处理它们)。但是,如果不能将此负载分散到集群上,那将是一种耻辱。此外,这似乎是一个非常优雅的解决方案
提前感谢您的帮助 您有一个解决方案,即读取是高度可压缩的,因此替换以下内容
res.append(tasks.process_read_pair.s(r1, r2))
借
在另一端调用pickle.load(zlib.decompress(obj))
如果DNA序列不够长的话,它应该能在足够长的DNA序列中赢得一个围绕大因子的因子。你可以在一个数组中按块对它们进行分组,然后进行转储和压缩
如果您还没有使用zeroMQ进行传输,那么另一个胜利就是使用zeroMQ进行传输
我不确定什么是进程字节,不是答案,但是太长了,不适合评论 让我们把问题缩小一点 首先,尝试跳过所有常规的逻辑/消息准备,只需对当前库进行尽可能紧密的发布循环。看看你能拿到多少钱。这将确定您的非队列相关代码是否有问题 如果仍然很慢,请设置一个新的python脚本,但使用它而不是芹菜。我在中端桌面上进行有用的工作(和json编码)时,成功地以超过6000/s的速度发布了它,所以我知道它的性能很好。这将确定问题是否与芹菜库有关。(为了节省您的时间,我从我的一个项目中截取了以下内容,希望在简化时不要破坏它…)
在上述两种方法之间,您应该能够将问题追溯到队列、库或代码中的一种。同样,不是答案,但注释太长。根据下面的评论/回答,我使用与我的应用程序相同的交换和路由设置了以下测试:
from amqplib import client_0_8 as amqp
try:
lConnection = amqp.Connection()
lChannel = lConnection.channel()
Exchange = 'celery'
for i in xrange(1000000):
lMessage = amqp.Message("~130 bytes of test data..........................................................................................................")
lMessage.properties["delivery_mode"] = 1
lChannel.basic_publish(lMessage, exchange=Exchange, routing_key='transient')
lChannel.close()
lConnection.close()
except Exception as e:
print e
你可以看到它一直在摇摆
我想现在该找出这与内部发生的事情之间的区别了。我将amqp添加到我的逻辑中,而且速度很快。FML
from amqplib import client_0_8 as amqp
try:
import stopwatch
lConnection = amqp.Connection()
lChannel = lConnection.channel()
Exchange = 'celery'
t = stopwatch.Timer()
files = [foo, bar]
f1 = open(files[0])
f2 = open(files[1])
res = []
count = 0
for r1, r2 in izip(FastqGeneralIterator(f1), FastqGeneralIterator(f2)):
count += 1
#res.append(tasks.process_read_pair.s(args=(r1, r2)))
#lMessage = amqp.Message("~130 bytes of test data..........................................................................................................")
lMessage = amqp.Message(" ".join(r1) + " ".join(r2))
res.append(lMessage)
lMessage.properties["delivery_mode"] = 1
lChannel.basic_publish(lMessage, exchange=Exchange, routing_key='transient')
if count == 1000000:
break
t.stop()
print "added %d tasks in %s" % (count, t)
lChannel.close()
lConnection.close()
except Exception as e:
print e
因此,我做了一个更改,将异步任务提交给Cellery in the loop,如下所示:
files = [foo, bar]
f1 = open(files[0])
f2 = open(files[1])
res = []
count = 0
for r1, r2 in izip(FastqGeneralIterator(f1), FastqGeneralIterator(f2)):
count += 1
res.append(tasks.process_read_pair.s(r1, r2))
if count == 10000:
break
t.stop()
g = group(res)
for task in g.tasks:
task.set(queue="transient")
result = g.delay()
res.append(tasks.speed.apply_async(args=("FML",), queue="transient"))
速度方法就是这样:
@app.task()
def speed(s):
return s
再次提交任务,我慢了
因此,这似乎与以下问题无关:
但是,它与函数的排队有关?!?!我很困惑。再一次,这不是答案,更多的是观察。通过简单地将后端从rpc更改为redis,我的吞吐量增加了三倍多:
重用producer实例可以提高性能:
with app.producer_or_acquire() as producer:
task.apply_async(producer=producer)
此外,任务可能是代理对象,如果是,则必须对每次调用进行评估:
task = task._get_current_object()
使用group
将自动重用制作者,这通常是您想要的
在循环中执行以下操作:
process_read_pair = tasks.process_read_pair.s
g = group(
process_read_pair(r1, r2)
for r1, r2 in islice(
izip(FastGeneralIterator(f1), FastGeneralIterator(f2)), 0, 1000)
)
result = g.delay()
你也可以考虑安装C代码> LababByMQ模块,用C语言编写。 如果可用,
amqp://
传输将自动使用它(或者可以使用librabbitmq://
手动指定):
pip install librabbitmq
直接使用基础库发布消息可能会更快
因为它会绕过芹菜路由助手等,但我不会
我觉得速度要慢得多。如果是这样的话,芹菜肯定有优化的空间,
因为到目前为止,我主要集中在优化消费者方面
还请注意,您可能希望在同一个t中处理多个DNA对