|
|
|
|
|
|
|
|
# 目标
|
|
|
本节使用一个爬虫任务和计算任务来展示如何追求代码的性能 。
|
|
|
充分理解线程、协程、进程、同步、异步、阻塞、非阻塞等概念,并能够根据具体场景选择合适的并发模型。
|
|
|
主线问题:如何解决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密集型任务时效率较低,总运行时间较长。
|
|
|
- **普通做法**效率最低,无法充分利用系统资源。
|
|
|
|
|
|
<style>
|
|
|
h1{font:20pt "SimHei", sans-serif;}
|
|
|
h2{font:16pt "SimHei", sans-serif;}
|
|
|
p{font:12pt "SimSun", sans-serif;line-height:150%}
|
|
|
li{font:12pt "SimSun", sans-serif;line-height:150%}
|
|
|
</style> |