V2EX mashiro233
mashiro233

mashiro233

V2EX 第 316689 号会员,加入于 2018-05-16 10:28:41 +08:00
根据 mashiro233 的设置,主题列表被隐藏
二手交易 相关的信息,包括已关闭的交易,不会被隐藏
mashiro233 最近回复了
@liuminghao233

你看他的说明,显然他说的"协程"和我们理解的协程不一样。

按照他的说明,只要做到方法级别的调用顺序控制就行了,连 yield 都没有(或许 return 就是 yield ?)。大概就是`异步代码有 event 来了直接调 callback `这一步就是“协程”调度器。

我们理解的协程就是参考 boost 的那个协程例子,能够在 foo 和 bar 函数里来回跳的那种切换,做到语句级的切换那种切换,让出后还有机会执行下面的语句。可能需要用到 setjmp 或者 ucontext 这种。
@wekw

破案了。
你这么一解释问题就解决了。
我们之间对协程这个定义理解的不一样。

我认为你所说的协程就是 goroutine,boost::coroutine 这种,很显然并不是。

举个 boost 的例子
```
void foo(){
std::cout << 1;
std::cout << 2;
std::cout << 3;
}

void bar(){
std::cout << a;
std::cout << b;
std::cout << c;
}

```
利用协程,我们可以让在单线程的情况下,通过 yield,能够让程序输出 a 1 b 2 c 3。这个是协程,能够在单线程的情况下切换 context。让它看起来像多线程。

你所说的“协程”,就是一种任务调度机制,你可以决定 foo 和 bar 的先后执行顺序输出可以是 1 2 3 a b c 或者 a b c 1 2 3。但是你无法做出 a 1 b 2 c 3 这种输出。

libuv 做的就是你说那些工作,给不同 socket 针对 read,write 等事件调用绑定的回调方法,并且还提供了 timer,线程安全的 sync 等常用工具。
@wekw

补充。在我看来,你是把协程和异步 api 混为一谈了。异步 api 能够使用协程的机制进行任务调度,但不是异步 api 必须使用协程。
@wekw

首先感谢你的回复,我确实不懂协程,我在工程中并没有使用过协程,不算 go 的话。
、我的之前回复语气可能有点冲,这里我道歉。我确实经验不足,毕竟我工作也没几年。既然 v2 是讨论技术的地方,那我继续向你请教。
另外你认错人了。

1.`第一操作系统会自动调控`
在我的理解里,单线程的调控就是把之前在 A 核的任务挪到 B 核。并没有解决一个核满了一个核空的问题。

2 `协程为什么性能高,就是因为没有上下文切换了`
我的理解里协程里依旧有上下文切换。因为需要保存当前程序运行在了哪一句。这个就是上下文,否则我无法理解协程是如何恢复到原来运行环境的,只不过开销比较小。可以话提供一个协程实现库让我学习一下。

3 ` 你管这个叫测试?好好学学英语,这是一个研究者在某一个会议上发表的论文`
可能是我的描述有误,这里的测试指的是论文里的测试数据。这篇链接是用来反驳你`是无法实现 10Gbps 和 40Gbps 的网卡速度的`这句话的。这篇文章依旧用的是 linux 网络栈,之后还有 bbr 的测试数据。根据里面的测试数据在 100Gbps 下达到了 79Gbps 的成绩。100Gbps 都能达到有个还可以的成绩,何来无法实现 40Gbps 一说?另外你给的文章里面也提到了 `使用多核编程技术替代多线程,将 OS 绑在指定核上运行` `而多核则是每个 CPU 核一个线程` `DPDK 程序启动后只能有一个主线程,然后创建一些子线程并绑定到指定 CPU 核心上运行`(这个是我贴的文档里描述)。这里面都提到了线程,只不过是用的线程池(不是无脑的开新线程还是将任务分配到线程池)。我还是没有看到里面提到了协程。

4. 同上

5.是我的描述有误,这里的 frame 指的是 ethernet frames。我没有说不是内核提供的,tun 本身就是依赖内核实现的。

6.我重新看了一遍,没有问题。goroutine 就是这么做的。

7. 我研究过 libuv 源码,也给 libuv 提交过 pr,我现在做项目基本上都是基于 libuv 做的。所以我才会说提供给 nodejs 异步的 api,文件操作用的是线程池实现的。另外,你说的文件指的是 File descriptor,这里是单纯的 file,也是描述不清的问题。

8.`你真的要好好学习一个。“协程的开销会比手动接管 callback 来的要高”,协程调度器的本质就是手动接管 callback,你还是不懂协程,唉。 `

我这里指的是,与其在 callback 处放协程调度器,不如自己实现一套更加轻量的调度器,参考 libuv,libuv 没有使用协程,老一点的 libevent 也没有使用协程。

总结下我的观点
1. 你的协程高性能这点,是得力于异步 api,和协程本身没有任何直接关系,不是协程造就了高并发高性能,协程是用来减轻异步 api 带来的开发难度。
2. 协程依旧需要切换上下文,因为需要恢复到 yield 的地方继续执行。
@wekw
不是

同样,这里不是你误导人的地方。这句话我还给你。

期待你的驳斥。
@c3824363
这个我可以补充一下。文件 io 这块,nodejs 的 Linux 异步文件 io 实现用的是线程池来做的。理由就是你说那样,不过 win 和 freebsd 我就不了解了,没开发经验。
@shijingshijing
arm64 是 普通参数放 r0-r7 浮点 /SIMD 是放 v0-v7 存不下的放 stack 上。
对于编译器来说,所有固定参数函数都可以当做可变参数函数来处理,函数的 arg 本质上是一个单向 list。所以 stdarg 里的那些函数你会发现完全可以用一个单向 list 来实现。
@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 来的要高。
c/c++由于其特殊性能够在非常多的场合上派上用场,所以不同领域的知识累积也不一样。

比如如果是服务器开发,可能会侧重偏向 tcp/ip 实现,网络模型这块。

如果是搞 linux 内核的,可能会偏指令集,用户层的 syscall 以及在内核里是如何实现等等。

如果是搞渲染器的,对 dx 和 opengl 这套东西肯定得熟了,shared 要怎么写,对指令集也得了解。必要时候还得手写汇编,毕竟渲染器这块对于效率有极高的要求。现在大多都是 arm 和 x86,资料多。上个世代各种偏门的 soc(说的就是 x360 和 ps3)可是把一堆写渲染器折腾的死去活来。

至于语言本身,我个人是不太倾向一开始于过度深入研究,应该关注于如何使用和用好轮子。随着项目的积累你会慢慢的去了解原理。我今年过年放假的时候研究了一个礼拜的元编程,到现在项目里也没啥地方需要用到。再比如楼上提到的 main 函数 printf 这些,如果你知道 crt0/1.o 是个什么东西,写过 ld script,移植过 newlib,闲的时候看过 musl libc 源码,回答这些个问题不是什么难事。如果对这块有兴趣,看优秀库的源码是个很好的提升手段,c++这块 boost 是个非常好的库,代码可读性很高。c 的话推荐 musl libc,也是可读性非常高的 c 标准库实现。同时有精力也可以写个 c 编译器玩玩。
2018-05-29 10:12:08 +08:00
回复了 sw0rd3n 创建的主题 程序员 有没有推荐的反向调试工具?
@sw0rd3n
哦,那是我看错了。反向调试应该和平台相关的,这个真不太清楚……
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1526 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 11ms UTC 16:33 PVG 00:33 LAX 09:33 JFK 12:33
Do have faith in what you're doing.
ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86