Rust异步编程 | 8lovelife's life
0%

Rust异步编程

大部分编程语言都支持异步编程,异步编程作为一种并发编程模型,可以使用少量的操作系统线程完成大量的并发任务。通过多线程也可以提高系统处理任务的效率。本文记录说明他们之间的差异与各自的优势,并会给出一个简单HTTP SERVER(Rust语言实现)多线程版本与异步版本的基准测试

并发模型

与常规的顺序编程相比,并发编程不那么标准化,不同语言有各自所支持的并发编程模型,并发性可以由不同的并发编程模型表达。以下为常见并发编程模型

OS threads

线程适用于少量任务的使用场景,线程通常附带CPU、内存的开销。线程间的切换是有成本的且很昂贵,即使是空闲的线程也在消耗系统资源。线程池可以解决一部分开销问题,但不能完全解决,使用线程不足以支持大量IO绑定型任务,如:服务器、数据库等。多线程对于现有代码的改造不依赖于特定的编程模型,不需要进行大量的代码重构就可以让现有的同步代码支持多线程

Event-driven programming

事件驱动编程与回调结合使用,可以有很好的性能表现,但通常会导致冗长的非线性控制流,很难遵循程序的数据流与错误传播

Coroutines

协程与线程一样,不需要特定的编程模型,很容易进行代码改造。与异步相似,能够支持处理大量的任务。协程抽象了对系统编程和自定义运行时实现器很重要的低级细节

The actor model

参与者模型将所有并发计算划分为称为参与者的单元,这些单元通过会出错的消息传递进行通信,就像在分布式系统中一样。参与者模型可以被高效实现,但它留下了许多实际问题,如流程控制和重试逻辑

Rust中的异步

异步可以降低CPU、内存开销,尤其对于大量IO绑定型任务的工作负载。同等条件下,可以比多线程处理更多的任务数量级。异步并不意味着一定比多线程好,对于小任务量的工作负载,多线程同样能够胜任。与其他编程语言不同,Rust的异步是惰性的,只有在发生 polled 的时候异步代码才会真正执行。Rust异步支持单线程,也支持多线程

HTTP SERVER 基准测试

使用WRK进行HTTP基准测试

1
2
3
4
5
6
7
docker run --rm  8lovelife/wrk -txxx -cxxx -d30 \
--timeout=15 http://host.docker.internal:port

-t: 线程数
-c: HTTP连接数
-d: 测试持续时间。-d30 表示持续测试30s
N/A: 宕机

ROUND 1

1
2
线程数:10
HTTP链接数:10
- QPS THROUGHPUT AVG MEMORY QUOTA CPU QUOTA TIMEOUT
V1 single-thread 0.63 19 12.23s 10M 2 0
V2 multi-thread 6.32 190 1.51s 10M 2 0
V3 thread-pool 6.29 189 1.52s 10M 2 0
V4 async single-thread 6.32 190 1.51s 10M 2 0
V5 async multi-thread 6.32 190 1.51s 10M 2 0

ROUND 2

1
2
线程数:100
HTTP链接数:100
- QPS THROUGHPUT AVG MEMORY QUOTA CPU QUOTA TIMEOUT
V1 single-thread 0.63 19 16.50s 10M 2 0
V2 multi-thread 63.21 1900 1.51s 10M 2 0
V3 thread-pool 6.61 199 11.67 10M 2 0
V4 async single-thread 63.22 1900 1.51 10M 2 0
V5 async multi-thread 63.18 1900 1.51 10M 2 0

ROUND 3

1
2
线程数:1000
HTTP链接数:1000
- QPS THROUGHPUT AVG MEMORY QUOTA CPU QUOTA TIMEOUT
V1 single-thread 0.63 19 16.50s 10M 2 0
V2 multi-thread NA N/A N/A 10M 2 N/A
V3 thread-pool 6.60 199 15.74s 10M 2 0
V4 async single-thread 614.44 18529 1.54s 10M 2 0
V5 async multi-thread 615.77 18565 1.54s 10M 2 0

ROUND 4

1
2
线程数:3000
HTTP链接数:3000
- QPS THROUGHPUT AVG MEMORY QUOTA CPU QUOTA TIMEOUT
V1 single-thread 0.73 23 16.50s 10M 2 5
V2 multi-thread NA N/A N/A 10M 2 N/A
V3 thread-pool 6.78 209 13.52s 10M 2 10
V4 async single-thread 673.95 20734 2.72s 10M 2 0
V5 async multi-thread 635.85 19738 3.49s 10M 2 0

ROUND 5

1
2
线程数:5000
HTTP链接数:5000
- QPS THROUGHPUT AVG MEMORY QUOTA CPU QUOTA TIMEOUT
V1 single thread 0.73 23 16.50s 10M 2 5
V2 multi-thread N/A N/A N/A 10M 2 N/A
V3 thread-pool 7.59 239 15.96s 10M 2 40
V4 async single-thread 593.99 20044 4.64s 10M 2 0
V4 async single-thread 911.34 28938 2.15s 20M 2 0
V5 async multi-thread N/A N/A N/A 10M 2 N/A
V5 async multi-thread 896.92 28978 2.42s 20M 2 0

参考

Asynchronous Programming in Rust

附录

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
version: '3.4'

services:
webserver_v1:
image: 8lovelife/webserver:v1
ports:
- 17878:7878
deploy:
resources:
limits:
cpus: '2'
memory: 10M

webserver_v2:
image: 8lovelife/webserver:v2
ports:
- 27878:7878
deploy:
resources:
limits:
cpus: '2'
memory: 10M

webserver_v3:
image: 8lovelife/webserver:v3
ports:
- 37878:7878
deploy:
resources:
limits:
cpus: '2'
memory: 10M

webserver_v4:
image: 8lovelife/webserver:v4
ports:
- 47878:7878
deploy:
resources:
limits:
cpus: '2'
memory: 20M

webserver_v5:
image: 8lovelife/webserver:v5
ports:
- 57878:7878
deploy:
resources:
limits:
cpus: '2'
memory: 20M

Sometimes you have to sacrifice everything you love to save everything you love. - Murph

Interstellar