lxdlam 最近的时间轴更新
lxdlam

lxdlam

Stay hungry, stay foolish
V2EX 第 91515 号会员,加入于 2015-01-13 12:44:59 +08:00
今日活跃度排名 5317
根据 lxdlam 的设置,主题列表被隐藏
二手交易 相关的信息,包括已关闭的交易,不会被隐藏
lxdlam 最近回复了
41 天前
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@lxdlam 基于终端 -> 基于中断
41 天前
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@ipwx 其实“非常准确明白这些并发方法”本身是一种提升了异步编程门槛的行为,这一串概念能引出来七八个名词和一大堆文章。我个人是非常乐意看见 goroutine 出现的,虽然确实是 dirty and hack 的,但是它足够简单,确确实实解决了实际问题(虽然我还是认为~~go 是垃圾~~)。

对于语言来说,提供一个尽可能统一的抽象,并让不同的 runtime 去实现不同的做法,再实现不同的生态,是一个非常好的做法,也是 Rust 社区正在采纳的做法。这确实会导致出现“事实标准”这一情况(比如 tokio 现在的绝对领导地位),但是给了用户在 consistent 的 interface 下,可以自主根据 workload 切换运行方式的自由。

实际上,阻塞和不阻塞不是异步问题的本源,归根到底还是我们希望每个任务都能够拿到足够多的时间片去跑,而不是要么被无用的 task 频繁占用 CPU ,要么在等一些其实没有意义事情而导致时间流失。从这两个角度出发,一方面我们尝试通过各种方式来更巧妙地让 task 在正确的时候和地方执行,而另一方面我们也在尽力去把各种有这种所谓“无用等待”的地方做成 pubsub 的事件行为把等待让出来,给其他 task 时间。说来也比较奇妙,CPU 和操作系统设计的时候已经采纳了基于终端的事件通知机制,但是应用层全面用到这个特性还是过去了很久。

至于 Worse is better 还是 Do the right thing ,我们都可以另起一次讨论了 :)。
41 天前
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@ipwx stackful 和 stackless 最大的区别不是保存调用栈的问题,是可不可以在任意函数中启动异步调用的问题,这个问题最经典的例子就是 js 所谓的有色函数,或者说 async/await 的传染问题。考虑一个非常经典的例子:

```
async function g() { return 123; }

function f(n) {
let x = (await g()) + n;
return x;
}
```

假定这个函数是可以运行的,那就有一个问题:`g` 要被调度走,肯定有其他函数要切入运行,栈要被更改,那当 `g` 执行完的时候,`x` 和 `n` 咋办?我们有两种思路:
1. 调用 `g` 的时候,我们记一下调用栈,这下 `f`, `x` 和 `n` 我都记下来了;
2. 让 `f` 也变成 `g` 一样的东西,这样 `f` 自己就会记录 `x` 和 `n` 的关系。

正好就是有栈和无栈协程的区别。

Project Loom 也打算把 stack 存下来放在 Java Heap 里面,搞成 stackful 的。至于 Fibers 为啥挂了,是因为 Fibers 把一个指挥内核调度的可能性给了用户,误用的可能性更高,比如可能跟系统的带锁操作形成死锁( fiber 在等一个 syscall 之后 yield ,但是系统在等 fiber 释放执行这个 syscall 的资源),也是一种优先级反转的特例。种种情况下,Fibers 虽然 API 没有被干掉,但是用户不咋用了。
41 天前
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@statumer 这是显然的强加因果。

网络编程的崛起之于协程已经是很早的事情了,实际上,nginx 和 Redis 都是单线程 IO 模型( Nginx 1.17 和 Redis 6 才支持的多线程网络 IO 模型),性能并不比后面很多所谓用了多线程 or 协程的应用差,那么协程在这里的作用是什么?从另一个角度,OpenResty 往 Nginx 里面嵌入了一个 LuaJIT ,底层 IO 逻辑完全没变的情况下,为什么比 Nginx 性能好那么多?这些和协程崛起相关没有任何直接联系。

关于二三点,既然你已经提到了 go ,为什么不去看看 go 的源码实现和 Scheduler 设计? Design Doc ( https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw/edit#heading=h.c3s328639mw9 )和 1.17 的源码 ( https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/runtime/proc.go;bpv=1;bpt=0 )都说明了 Go 的工作方式正是我所说的在多核系统下,会用另一个线程去跑 blocking syscall ,对于你的程序来说,这部分 syscall 就是 non-blocking 的。同样在 Java 最新的协程方案 Project Loom 里面,工作方式也是一样的 ( https://blogs.oracle.com/javamagazine/post/going-inside-javas-project-loom-and-virtual-threads )。特别要提示一点,虽然现在 Go 确实在可以 poll 读文件的情况下使用 epoll ,但是在 bsd 环境下 Go 现在还是使用 blocking syscall ,而更早的 Go 版本(我没记错是 1.12 以及之前)同样使用的 blocking 的文件 syscall 去读文件。

实际上,如果你真的对所谓的“只是对有无调用栈的取舍”做出了理解的话,你就不会陷入这种强加因果的关系。对于传统的线程,我们会有两种程度的开销:一个是线程自身占用 memory 的开销,这个和系统的默认栈大小有关,这导致了我们的内存占用会随着线程申请数量,这个线程自身的申请也需要通过 syscall ,时间和内存资源占用都存在 overhead ;另一个角度,虽然根据机器和系统不同数据有差异,但是普遍来说,native thread 切换的开销并不小,对于 常用 Linux 发行版来说,这个切换通常不少于 1ms 。当我们实现了用户侧调度,我们可以通过巧妙地 GC 管理等减少这种异步结构的申请和释放开销,通过剔除和当前应用无关的系统 OS 数据字段同步达到更快的上下文切换,甚至直接基于状态机模型舍弃这部分开销,得到普遍更好的执行时间。更进一步来说,OS Level 的调度是 generic 的,内核针对的是任何任务的调度,而对于我们的应用,我们和 runtime 更容易知道我们的调度侧重点和优势,能够把时间和优先级排布做的更精妙,自然能够带来一定的性能提升,这才是用户侧调度的意义所在,也是协程这类结构的性能提升所在。

我同意异步的 IO 会比起同步 IO 有性能提升,虽然平台不同变量较大,但在普遍比较下 IOCP 确实性能和系统利用率是强于 epoll 的,io_uring 确实是未来的方向。但是这部分的性能提升和线程以及协程本身开销优化没有任何关系,假如我们认为线程和协程切换都是零开销的话,那使用哪种结构都影响不了在同一种 IO 下的调度开销。顺带补充一点,线程的上下文切换和调度也是要 involve syscall 的,用户侧实现调度的话能够减少这些 syscall 的次数。

最后,异步是一个很复杂的概念,但是我们可以抛弃一些严谨性的情况下认为异步就是并发。对于并发来说,根据你观察的 level 不同,多进程对于操作系统来说是并发的,多线程对于应用是并发的,而我多次提到,所谓的协程就是一种用户调度抽象,实际上和多线程相比就是执行模型的差距,本质上和多线程没有区别。

最后,如果你认为在当时文章里描述的 LWP 和 Linux Thread 的调度没有任何区别的话,那可能不求甚解的是你。Java 1.3 是 2002 年发布的,而 Linux 真正实现了内核可抢占是在 2003 年的 2.6 版本。由于 1:N 的设计,当某个 task 开始调用 blocking syscall 的时候,其他的所有 task 都没有机会被唤醒,更别提调度到其他 core 去执行;而对于 Solaris 的调度,LWP 可以任意被调度到可用的 kernal thread 上,进而有机会被调度到其他 CPU core 上( https://www.usenix.org/legacy/publications/library/proceedings/sa92/eykholt.pdf )。这些历史背景才是当时选择的原因。当然,Linux 现在对实时性的支持已经非常好了,所以 Project Loom 重拾历史积淀,再次把用户侧线程引入 Java 。
42 天前
回复了 publicly 创建的主题 2021 你呢?今年最大的改变是什么?
1. 买了很多以前买不起但是很喜欢的东西;
2. 玩了更多优秀的游戏,看了不少 GDC 演讲,了解了很多以前不知道的技术细节;
3. 又开始写文章了。
42 天前
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@statumer 协程能不能火起来跟 epoll 没有任何直接联系。

epoll 的出现是因为需要解决 IO 过程中信号等待的问题,能把时间交出来去干一些其他事情,这也就是我说的这个“在内核态实现的调度反而还有点类似协程的味道”。而 syscall 或者 IO 阻塞和协程本身没有任何联系,这都是因为协程是一个用户侧调度的 continuation ,系统层面只能感知到 thread ,如果多个协程按照 1:N 模型跑在同一个线程里面,任意一个有阻塞的 syscall 一定会 block 住,和你是否使用协程无关,反过来说,在使用 epoll 的情况下,把 fd 注册完后扔到一个线程里面去等待,跟你是否使用协程也没有本质的性能区别(如果无视线程切换开销的话)。换句话说,如果我们是一个多核系统,完全可以引入第二个线程在不同的核来做轮询同步 IO ,并在成功后向第一个线程发送消息,对于第一个线程来说,这个过程也是 non-blocking 的,和使用线程或者协程同样没有关系。epoll 就相当于这个“第二个线程”,只是由 Kernel 在机制上做了优化,保证了在单核上也能执行这种高效操作。

如果你要谈为什么 Java 1.1 引入 Green Thread 但是 Java 1.3 之后废弃了 Green Thread ,Oracle 的官方文档解释了这个问题 https://web.archive.org/web/20040211225937/http://java.sun.com/developer/technicalArticles/Programming/linux/,最重要的问题在于 1.1 引入 Green Thread 是配合了 Solaris 的 LWP 的实现,而这个 1:N model 在 Linux 下是做不到真实的并行的,而不是所谓的 syscall 阻塞问题。

协程快不快的问题核心是 focus 在用户调度和内核调度带来的各种切换问题,在不同的执行模型、结构模型下天差地别,而不是简单的异步操作的问题。
44 天前
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
@ipwx Erlang 的 Process 也是很经典的 Actor 实现,在爱立信已经跑了 20 年了
44 天前
回复了 kingofzihua 创建的主题 Linux 问一个协程方面的问题
“协程”是一个大而空的筐,啥鸡蛋都往里装。Go 的 Goroutine 是 stackful 且在 Go 1.14 之后是基于 signal 的抢占式调度的,Kotlin 是 stackless 但是协作调度的,但是这俩都有人叫做协程。

实际上的“协程”,是一种用户调度抽象。有基本 OS 概念的人都知道,进程( Process )是负责资源隔离的,而真正的运行调度单元是线程( OS Thread )。线程不是免费的,这导致我们如果创建一大堆线程,必定涉及了资源的扩张,而实际上的很多 task 虽然对实时性、执行效率有要求,却不需要这么多资源(比如 IO 任务),我们该如何让这些任务能够更好地执行,而不每次都开一个线程呢?这就是协程的本来目的。

通俗来说,一个语言有没有协程,并不意味着这个语言没有这种抽象调度能力。Lisp 系的 call/cc 和 C 的 setjmp/longjmp 也可以实现某种程度上的协程,甚至更简单的,只要一个语言有能够将一个运行过程封装到一个内存结构上的能力(例如,C++ 有 operator() 的 struct ),基于这个能力写一个 scheduler ,你也发明了一个协程。

协程为何性能高?这也是一个网络迷思:如果你的 task 如上面描述的,是一个低资源占用但是高实时消耗的,那当然是显而易见的性能提升(线程内核态调度延迟一般是比协程用户态调度更慢的),但是这不是银弹,如果你的 task 需要独占资源,那么你的线程调度同样能做到跟协程一样快。

那么,我们该如何认识协程?实际上来说,这是一个异步计算结构的问题:
1. 抢占式调度( preemptive ) vs 协作式调度( cooperative )
2. 有栈结构( stackful ) vs 无栈结构( stackless )
3. 跟系统 Thread 的 mapping 关系:1:1, N:1 和 N:M
等等。这些关键词网络上都有非常优秀的资料,就轮不到我在这里扯淡了。认识协程最关键的部分,实际上还是看语言提供的能力(语言结构、标准库等),以及语言的目标领域。

楼主举得 NIO 的例子实际上跟这个话题相去甚远:NIO 用的是 kernel 提供的 IO Multiplexing 或者 Async IO 能力( epoll/kqueue/io_uring/IOCP ),这个在内核态实现的调度反而还有点类似协程的味道。
69 天前
回复了 Mohanson 创建的主题 分享发现 当得知有一笔亿计数的美元遗产...
育碧?
关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2404 人在线   最高记录 5497   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 9ms · UTC 12:25 · PVG 20:25 · LAX 04:25 · JFK 07:25
Developed with CodeLauncher
♥ Do have faith in what you're doing.