问:
“我经常不得不...加快速度
的最简单和最好的方法是什么?”
答:
最简单(评论的方法不是)和最好
的方法是至少:(a)最小化所有开销(50k 次- 实例化成本是此类成本之一),(b)利用令人尴尬的独立性(然而, not a being a True- ) in process-flow (c)尽可能接近 just- ,latency-masked process-flow的出血边缘
Thread
[PARALLEL]
[CONCURRENT]
鉴于
简单性和性能似乎都是“最佳”的衡量标准:
任何成本,首先不能证明通过大幅提高性能来介绍自己的成本是合理的,其次,不能对性能产生额外的积极净效应(加速),都是性能反模式和不可原谅的计算机科学罪。
因此
,我无法推广使用 GIL 锁(按设计,甚至只是[CONCURRENT]
阻止处理)绑定和性能令人窒息的逐步循环步进任意数量的 Python 线程一个接一个- after-another-...-re- [SERIAL]
ised 大约100 [ms]
-quanta 的代码解释时间链阻止了一个且只有一个这样的 Python 线程被允许运行(所有其他线程都被阻塞等待......而是一种性能反模式,不是吗?),
所以
宁愿选择基于进程的工作流并发(性能提升很多,大约 50k url-fetches,其中数百 / 数千 [ms] 延迟(协议和安全握手设置 + 远程 url-decode + 远程内容组装 + 远程内容到协议封装 + 远程到本地网络流 + 本地协议解码 + ...)。
草图流程框架:
from joblib import Parallel, delayed
MAX_WORKERs = ( n_CPU_cores - 1 )
def main( files_in ):
""" __doc__
.INIT worker-processes, each with a split-scope of tasks
"""
IDs = range( max( 1, MAX_WORKERs ) )
RES_if_need = Parallel( n_jobs = MAX_WORKERs
)( delayed( block_processor_FUN #-- fun CALLABLE
)( my_file, #---------- fun PAR1
wPROC #---------- fun PAR2
)
for wPROC in IDs
)
def block_processor_FUN( file_with_URLs = None,
file_from_PART = 0
):
""" __doc__
.OPEN file_with_URLs
.READ file_from_PART, row-wise - till next part starts
- ref. global MAX_WORKERs
"""
...
这是__main__
生成足够多的工作进程的初始 Python 解释器端技巧,它开始my_file
独立地爬取 URL-s 的“列表”,并且确实开始了工作流程,[CONCURENT]
其中一个独立于任何其他工作流程。
通过block_processor_FUN()
引用传递给工作人员的 , 确实简单地打开了文件,并开始仅获取/处理其“自己的”部分,即从( wPROC / MAX_WORKERs )
到( ( wPROC + 1 ) / MAX_WORKERs )
它的行数。
就这么简单。
如果愿意调整一些 URL 可能需要花费更长的极端情况,那么可能会改进负载平衡公平队列的形式,但代价是更复杂的设计(许多进程到进程的消息队列可用),有一个{ __main__ | main() }
-side FQ/LB-feeder 并使工作进程从此类作业请求 FQ/LB-facility 检索他们的下一个任务。
更复杂和更健壮的 URL 服务持续时间分布不均匀“跨越”my_file
要服务的 URL-s 的有序列表。
影响最终性能/稳健性的简单性/复杂性级别的选择是您的。
有关更多详细信息,您可能希望阅读此内容和代码,以及进一步提高性能的示例或提示。