@
wekw 看了你的回答,有些地方没看懂请教一下。
1.`实际上我们知道一个 CPU 核心微观上在任一个时刻都只运行一个指令`
我从搞网络编程开始,就没有碰到过单 cpu 的服务器。最差的也是 4 核 8 线程起步。我们都知道在做 epoll 模式的编程的时候,想充分发挥 cpu 性能,通常的做法要么用线程池,要么多开几个 process,在 bind 时候使用 SO_REUSEPORT 让内核做负载均衡。协程是单线程,这种情况下你就让其他几个 CPU 核看戏?
2. `Linux 下线程就是一堆共用内存的进程,需要上下文切换:将寄存器的内存暂存到内存。`
首先协程的切换就不要消耗资源了?协程也要保存上下文的啊,也要切换啊。这点我很赞同 @
c3824363,epoll 等机制都给你提供了一个 user_ptr 这种保存上下文的东西了。为什么要再封一套协程降切换上下文低性能?
3. `而一次内存读写需要 70-90 ns,是无法实现 10Gbps 和 40Gbps 的网卡速度的。`
https://meetings.internet2.edu/media/medialibrary/2016/10/24/20160927-tierney-improving-performance-40G-100G-data-transfer-nodes.pdf这里测试了 100G 网络的测试,系统是 Centos7 机器是 Dell z9100 跑 linux 系统。测试出来结果是 100Gbps 下 70G。
4. `于是 DPDK 诞生了,在用户态使用单线程的方式来阻止上下文切换`
DPDK 和协程又有什么关系? DPDK 对任务的做法是 poll+线程池,文档里写的很清楚。
https://dpdk.org/doc/guides/prog_guide/env_abstraction_layer.html 3.1.1 节
The core initialization and launch is done in rte_eal_init() (see the API documentation). It consist of calls to the pthread library (more specifically, pthread_self(), pthread_create(), and pthread_setaffinity_np()).
5.`自己构造协程调度器,等于自己又在图灵机模型下实现了一遍多任务调度,成功实现了高性能 SDN:软件定义网络。`
SDN 和协程又有什么关系吗?如果说是在用户层实现 tcp/ip 栈,我自己也用 linux 的 tun 网卡直接收发 frame 提供给 tcp/ip 栈,并且能够成功的接入互联网。没有任何地方用到协程。
6. `而协程这种软件实时调度的纯异步是非常难以理解的,这才是协程最难的地方。`
纯异步和协程又有什么关系?异步是因为你调用了异步 API,那是异步 API 的功劳。协程让出后,除非被 resume 是不会用工作的,在网络编程那块,协程的调度器帮你解决了异步回调要解决的问题,你只需要按照多线程的思想去写程序就行了。举个例子
```
something = read()//这里是阻塞 api yield 依旧是要等有数据可读之后才会让出,该阻塞的还是阻塞。一直没有数据就卡死在这了。
yield()
do_something
```
```
try_read_async(call scheduler resume) //因为使用了异步 api,所以在调用时候不会阻塞,会立即执行。当有数据的时候,回调函数会通知调度器,告诉你可以从 buffer 里取数据了恢复协程运行。
yield()
something = read_from_buffer()
do_something()
```
第二种异步方法是典型 epoll 应用开发模式,刚好 @
liuminghao233 提到了 boost,我记得 boost 的 asio 的 api 就是这么设计的。稍微智能一些的协程比如说 goroutine,你都不需要手动 yiled,都给你封装好了。但是为什么有的情况下明明有了 goroutine 还是要使用 epoll ?就是因为即便 goroutine 是协程依然有开销,虽然没有线程大但是并不是接近 0 (
https://medium.freecodecamp.org/million-websockets-and-go-cc58418460bb )。协程依旧有开销,100k 个协程就要保留 100k 个上下文,还要处理调度器。
所以,我不明白 `协程产生的目的是提高性能` 这个结论是怎么得出来的。协程的存在最早就是用于任务调度的,因为需要手动 yield 后来被时间片替代了(参考我第一个例子,假设一个任务永远没有让出那么就卡在那了)。后来由于异步 api 的出现,使得协程能够实现 “看起来像多线程”的编程方式,使得需要写回调处理的场景可以用看起来像多线程的方式去解决(继续看向 goroutine )。但是比起直接接管回调的方式,依然还是有开销,c1000k 协程的开销依然很大。
所以你所提到的协程目的是为了提高性能的观点我无法认同,所谓的提高性能那是因为用了异步 api,是内核的功劳。和协程没有任何关系。并且在极限环境下,协程的开销会比手动接管 callback 来的要高。