# 目标 本节使用一个爬虫任务和计算任务来展示如何追求代码的性能 。 充分理解线程、协程、进程、同步、异步、阻塞、非阻塞等概念,并能够根据具体场景选择合适的并发模型。 主线问题:如何解决IO和计算速度不匹配、如何任务分解、分发和协作 。 # 任务 ## 1、CPU密集型/计算密集型:   将```sum(i * i for i in range(10**8))```作为一个子任务,运行5次,分别使用单线程、多线程、多进程、多协程、异步等方式,并对比运行时间、内存使用、CPU使用等指标。 ## 2、IO密集型: # 讨论分析 ## 1、CPU密集型/计算密集型:   为控制变量,代码在同一台设备上运行,且关闭其他进程,保证CPU资源不被其他进程占用。测试结果如下: ### 普通做法: #### 运行前:   CPU使用率:0.0%,内存使用率:53.3% #### 运行后:   CPU使用率:0.6%,内存使用率:53.0%,内存使用:0.003336MB,峰值内存使用:0.004216MB,单个子任务运行平均时间:7.86s,总运行时间:39.31s #### 运行结束30秒后:   CPU使用率:0.6%,内存使用率:53.0% ### 多进程: #### 运行前:   CPU使用率:0.0%,内存使用率:52.8% #### 运行后:   CPU使用率:1.3%,内存使用率:52.9%,内存使用:0.096089MB,峰值内存使用:0.121865MB,单个子任务运行平均时间:0.654s,总运行时间:1.86s #### 运行结束30秒后:   CPU使用率:0.6%,内存使用率:52.7% ### 多线程: #### 运行前:   CPU使用率:3.8%,内存使用率:53.1% #### 运行后:   CPU使用率:3.6%,内存使用率:53.1%,内存使用:0.001472MB,峰值内存使用:0.019296MB,单个子任务运行平均时间:38.89s,总运行时间:39.77s #### 运行结束30秒后:   CPU使用率:0.7%,内存使用率:52.9% ### 多线程 + 线程池: #### 运行前:   CPU使用率:0.0%,内存使用率:52.9% #### 运行后:   CPU使用率:0.7%,内存使用率:53.1%,内存使用:0.000952MB,峰值内存使用:0.036604MB,单个子任务运行平均时间:40.49s,总运行时间:41.16s #### 运行结束30秒后:   CPU使用率:0.6%,内存使用率:52.9% ### 多协程: #### 运行前:   CPU使用率:1.1%,内存使用率:53.4% #### 运行后:   CPU使用率:0.2%,内存使用率:53.4%,内存使用:0.009448MB,峰值内存使用:0.011027MB,单个子任务运行平均时间:9.14s,总运行时间:45.70s #### 运行结束30秒后:   CPU使用率:0.6%,内存使用率:53.4% ### 多进程 + 异步: #### 运行前:   CPU使用率:0.8%,内存使用率:54.9% #### 运行后:   CPU使用率:0.6%,内存使用率:54.9%,内存使用:0.123481MB,峰值内存使用:0.188774MB,单个子任务运行平均时间:0.63s,总运行时间:1.85s #### 运行结束30秒后:   CPU使用率:0.9%,内存使用率:55.0% ## 2、IO密集型:   为控制变量,代码在同一台设备上运行,关闭其他进程,并在相同的网络环境下运行,保证网络资源不被其他进程占用。 ### 普通做法: #### 运行前:   CPU使用率:0.0%,内存使用率:52.5% #### 运行后:   CPU使用率:0.9%,内存使用率:52.6%,内存使用:0.074968MB,峰值内存使用:4.162339MB,总运行时间:5.56s #### 运行结束30秒后:   CPU使用率:0.6%,内存使用率:52.5% ### 多进程: #### 运行前:   CPU使用率:0.0%,内存使用率:52.7% #### 运行后:   CPU使用率:5.0%,内存使用率:52.8%,内存使用:0.03219MB,峰值内存使用:0.085236MB,总运行时间:5.81s #### 运行结束30秒后:   CPU使用率:1.2%,内存使用率:53.1% ### 多线程: #### 运行前:   CPU使用率:0.0%,内存使用率:52.9% #### 运行后:   CPU使用率:0.1%,内存使用率:53.0%,内存使用:MB,峰值内存使用:MB,总运行时间:1.63s #### 运行结束30秒后:   CPU使用率:0.5%,内存使用率:52.9% ### 多线程 + 线程池: #### 运行前:   CPU使用率:1.3%,内存使用率:53.2% #### 运行后:   CPU使用率:1.3%,内存使用率:53.1%,内存使用:0.092493MB,峰值内存使用:4.371028MB,总运行时间:1.47s #### 运行结束30秒后:   CPU使用率:0.8%,内存使用率:53.2% ### 多协程: #### 运行前:   CPU使用率:1.1%,内存使用率:54.4% #### 运行后:   CPU使用率:0.8%,内存使用率:54.4%,内存使用:0.753684MB,峰值内存使用:15.09151MB,总运行时间:1.45s #### 运行结束30秒后:   CPU使用率:0.8%,内存使用率:54.4% ### 多进程 + 异步: #### 运行前:   CPU使用率:1.5%,内存使用率:60.0% #### 运行后:   CPU使用率:0.8%,内存使用率:59.8%,内存使用:0.569592MB,峰值内存使用:6.360097MB,总运行时间:7.40s #### 运行结束30秒后:   CPU使用率:1.3%,内存使用率:59.6% # 总结 ## 1、CPU密集型/计算密集型: | | 普通做法 | 多进程 | 多线程 | 多线程+线程池 | 多协程 | 多进程+异步 | | :----------------------: | :--------: | :--------: | :--------: | :-----------: | :--------: | :---------: | | 运行前CPU使用率 | 0.0% | 0.0% | 3.8% | 0.0% | 1.1% | 0.8% | | 运行后CPU使用率 | 0.6% | 1.3% | 3.6% | 0.7% | 0.2% | 0.6% | | 运行结束30秒后CPU使用率 | 0.6% | 0.6% | 0.7% | 0.6% | 0.6% | 0.9% | | 运行前内存使用率 | 53.3% | 52.8% | 53.1% | 52.9% | 53.4% | 54.9% | | 运行后内存使用率 | 53.0% | 52.9% | 53.1% | 53.1% | 53.4% | 54.9% | | 运行结束30秒后内存使用率 | 53.0% | 52.7% | 52.9% | 52.9% | 53.4% | 55.0% | | 内存使用 | 0.003336MB | 0.096089MB | 0.001472MB | 0.000952MB | 0.009448MB | 0.123481MB | | 峰值内存使用 | 0.004216MB | 0.121865MB | 0.019296MB | 0.036604MB | 0.011027MB | 0.188774MB | | 单个子任务运行平均时间 | 7.86s | 0.654s | 38.89s | 40.49s | 9.14s | 0.63s | | 总运行时间 | 39.31s | 1.86s | 39.77s | 41.16s | 45.70s | 1.85s |   根据表格中的数据,我们可以对不同的并发和并行编程方法进行详细的总结: ### 普通做法 - **CPU使用率**:在任务运行前、运行后和运行结束30秒后的CPU使用率都较低,表明普通做法没有充分利用CPU资源。 - **内存使用率**:内存使用率变化不大,表明普通做法对内存的影响较小。 - **内存使用**:内存使用量和峰值内存使用量都很低。 - **运行时间**:单个子任务和总任务的运行时间都较长,表明普通做法效率较低。 ### 多进程 - **CPU使用率**:在任务运行后CPU使用率显著增加,表明多进程方法能够充分利用多核CPU的优势。 - **内存使用率**:内存使用率略有增加,但变化不大。 - **内存使用**:内存使用量和峰值内存使用量较高,表明多进程方法对内存的需求较大。 - **运行时间**:单个子任务和总任务的运行时间都显著减少,表明多进程方法在处理CPU密集型任务时效率最高。 ### 多线程 - **CPU使用率**:在任务运行前、运行后和运行结束30秒后的CPU使用率都较低,表明多线程方法没有充分利用CPU资源。 - **内存使用率**:内存使用率变化不大。 - **内存使用**:内存使用量和峰值内存使用量较低。 - **运行时间**:单个子任务和总任务的运行时间都较长,表明多线程方法在处理CPU密集型任务时效率较低。 ### 多线程+线程池 - **CPU使用率**:在任务运行后CPU使用率略有增加,但变化不大。 - **内存使用率**:内存使用率变化不大。 - **内存使用**:内存使用量和峰值内存使用量较低。 - **运行时间**:单个子任务和总任务的运行时间都较长,表明多线程+线程池方法在处理CPU密集型任务时效率较低。 ### 多协程 - **CPU使用率**:在任务运行前、运行后和运行结束30秒后的CPU使用率都较低,表明多协程方法没有充分利用CPU资源。 - **内存使用率**:内存使用率变化不大。 - **内存使用**:内存使用量和峰值内存使用量较低。 - **运行时间**:单个子任务和总任务的运行时间都较长,表明多协程方法在处理CPU密集型任务时效率较低。 ### 多进程+异步 - **CPU使用率**:在任务运行后CPU使用率显著增加,表明多进程+异步方法能够充分利用多核CPU的优势。 - **内存使用率**:内存使用率略有增加,但变化不大。 - **内存使用**:内存使用量和峰值内存使用量较高,表明多进程+异步方法对内存的需求较大。 - **运行时间**:单个子任务和总任务的运行时间都显著减少,表明多进程+异步方法在处理CPU密集型任务时效率最高。 ### 结论 - **多进程**和**多进程+异步**方法在处理CPU密集型任务时效率最高,能够充分利用多核CPU的优势,但对内存的需求较大。 - **多线程**和**多线程+线程池**方法在处理CPU密集型任务时效率较低,主要是由于Python的全局解释器锁(GIL)的存在,无法实现真正的并行。 - **多协程**方法在处理CPU密集型任务时效率较低,但在处理I/O密集型任务时可能会有更好的表现。 - **普通做法**效率最低,无法充分利用系统资源。 ## 2、IO密集型: | | 普通做法 | 多进程 | 多线程 | 多线程+线程池 | 多协程 | 多进程+异步 | | :----------------------: | :--------: | :--------: | :----: | :-----------: | :--------: | :---------: | | 运行前CPU使用率 | 0.0% | 0.0% | 0.0% | 1.3% | 1.1% | 1.5% | | 运行后CPU使用率 | 0.9% | 5.0% | 0.1% | 1.3% | 0.8% | 0.8% | | 运行结束30秒后CPU使用率 | 0.6% | 1.2% | 0.5% | 0.8% | 0.8% | 1.3% | | 运行前内存使用率 | 52.5% | 52.7% | 52.9% | 53.2% | 54.4% | 60.0% | | 运行后内存使用率 | 52.6% | 52.8% | 53.0% | 53.1% | 54.4% | 59.8% | | 运行结束30秒后内存使用率 | 52.5% | 53.1% | 52.9% | 53.2% | 54.4% | 59.6% | | 内存使用 | 0.074968MB | 0.03219MB | N/A | 0.092493MB | 0.753684MB | 0.569592MB | | 峰值内存使用 | 4.162339MB | 0.085236MB | N/A | 4.371028MB | 15.09151MB | 6.360097MB | | 总运行时间 | 5.56s | 5.81s | 1.63s | 1.47s | 1.45s | 7.40s |   根据表格中的数据,我们可以对不同的并发和并行编程方法进行详细的总结: ### 普通做法 - **CPU使用率**:在任务运行前、运行后和运行结束30秒后的CPU使用率都较低,表明普通做法没有充分利用CPU资源。 - **内存使用率**:内存使用率变化不大,表明普通做法对内存的影响较小。 - **内存使用**:内存使用量和峰值内存使用量都很低。 - **运行时间**:总运行时间为5.56秒,表明普通做法效率较低。 ### 多进程 - **CPU使用率**:在任务运行后CPU使用率显著增加,表明多进程方法能够充分利用多核CPU的优势。 - **内存使用率**:内存使用率略有增加,但变化不大。 - **内存使用**:内存使用量和峰值内存使用量较低。 - **运行时间**:总运行时间为5.81秒,略高于普通做法,表明多进程方法在处理IO密集型任务时效率较高。 ### 多线程 - **CPU使用率**:在任务运行前、运行后和运行结束30秒后的CPU使用率都较低,表明多线程方法没有充分利用CPU资源。 - **内存使用率**:内存使用率变化不大。 - **内存使用**:内存使用量和峰值内存使用量较低。 - **运行时间**:总运行时间为1.63秒,显著低于普通做法,表明多线程方法在处理IO密集型任务时效率较高。 ### 多线程+线程池 - **CPU使用率**:在任务运行后CPU使用率略有增加,但变化不大。 - **内存使用率**:内存使用率变化不大。 - **内存使用**:内存使用量和峰值内存使用量较低。 - **运行时间**:总运行时间为1.47秒,显著低于普通做法,表明多线程+线程池方法在处理IO密集型任务时效率较高。 ### 多协程 - **CPU使用率**:在任务运行前、运行后和运行结束30秒后的CPU使用率都较低,表明多协程方法没有充分利用CPU资源。 - **内存使用率**:内存使用率变化不大。 - **内存使用**:内存使用量和峰值内存使用量较高。 - **运行时间**:总运行时间为1.45秒,显著低于普通做法,表明多协程方法在处理IO密集型任务时效率较高。 ### 多进程+异步 - **CPU使用率**:在任务运行后CPU使用率显著增加,表明多进程+异步方法能够充分利用多核CPU的优势。 - **内存使用率**:内存使用率略有增加,但变化不大。 - **内存使用**:内存使用量和峰值内存使用量较高。 - **运行时间**:总运行时间为7.40秒,略高于普通做法,表明多进程+异步方法在处理IO密集型任务时效率较低。 ### 结论 - **多线程**、**多线程+线程池**和**多协程**方法在处理IO密集型任务时效率最高,能够显著减少总运行时间。 - **多进程**方法在处理IO密集型任务时效率较高,但略低于多线程和多协程方法。 - **多进程+异步**方法在处理IO密集型任务时效率较低,总运行时间较长。 - **普通做法**效率最低,无法充分利用系统资源。