为什么要这么快?
#scheduler #go #性能 #并发性

Go以快速而闻名。实际上,GO的出色性能在并发任务中使用时表现出最好的表现。尽管许多方面都可以在同时任务中表现良好,但在GO中有一个特别重要的事情非常重要。 GO帮助基础操作系统,并对某些繁重的工作负责。

免责声明:这篇文章在每个细节上并不意味着实际上是正确的,也不是解释与该主题相关的所有内容。这只是简化一个彻底概念的尝试。

要了解此帮助的范围,一个简短的上下文是按顺序进行的。一台PC上有很多硬件级CPU内核,例如。 8英特尔Core-I7处理器上的8逻辑CPU内核。但是,当您启动PC时,可能有数百个流程同时运行。怎么可能?每个突出的操作系统都有一个任务调度程序的任务是将处理器的资源分配给线程。线程是包含有序的机器指令集的执行途径,每个过程都有一个或多个。

通常,线程可以在三个不同的状态中:在当前在CPU核心上运行时,它在中执行。当它准备好执行时,它在 状态中,并且在停止时处于AA wating 状态中,等待发生的事情,例如一个I/O操作,不能运行。线程在这些状态之间进行切换,当它们准备就绪时,调度程序将它们指定为要执行的CPU核心上的时间插槽。幼稚的调度程序只需将核心的插槽均匀地拆分,并给每个可运行的线程一个类似的共享。例如,如果核心的插槽为1,并且有100个可运行的线程,则每个线程将获得核心计算时间的10ms。

OS Scheduler

如果有线程要运行,调度程序确保没有任何核心会保持空闲。调度程序从CPU核心处取出线程并替换另一个要执行的过程称为上下文开关。关键是,上下文切换非常昂贵。据说每个上下文开关都需要等同的时间运行数千台机器说明。

存在两种类型的线程。有些线程永远不会遇到某种程度,他们应该等待某个系统调用或网络请求请求在进行进一步之前就可以完成并且总是可以运行的。这些线程中的指令序列可以无缝向前推动,而对他们可以执行的事实的唯一限制是CPU的计算能力。因此,它们被称为 cpu结合线。自然,还有另一种类型的线程,偶尔会因某些IO任务而停止,并且直到完成IO任务完成后才能继续。也就是说,他们沿途进入等待状态,当他们这样做时,它们会被操作系统调度程序切换。这些称为 io-bond 线程。

通常,在通过单个进程执行并发任务时,使用多线程会促使编程语言的运行时将任务分为线程,并让OS管理执行方式。如果线程是io-bond的,则调度程序的上下文开关将使它们在同行中几乎运行,因此与幼稚的方法相比,当线程在等待时,核心将保持空闲状态的性能激增IO任务。但是,如果此过程在每个核心上都有多个线程,并且所有这些线程都限制在CPU上,则随后的上下文开关将导致该过程的性能急剧下降。这就是为什么同时在多个线程(例如一百万)上同时运行简单的CPU结合计算不是一个好主意的原因。

事实是,当操作系统调度程序接收线程时,无论螺纹的类型如何运行,上下文切换在开销上。在应用程序级别上,在上下文开销开销和程序的空闲时间之间,通过线程数量进行了权衡。使上下文切换成本效益的原因是,避免闲置核心的收益是巨大的,当涉及到IO结合线程时。更重要的是,上下文切换将使不同的过程看起来像是并行运行。这些使开销在操作系统级别不可避免。

但是,如果我们可以在应用程序级别缓解这样的开销怎么办?运行时对应用程序的线程具有更多的控制。如果一个人可以利用运行时的特权在其线程上更有效地执行上下文开关,则开销将下降,并且性能会改善。 GO的运行时调度程序显然具有相同的心态。

go在其运行时引入了一个新的抽象层,以从操作系统线程切换有序的任务,执行线程。 GO的概念模型称逻辑CPU核心为处理器或(p)和A螺纹机器或(M)。执行线称为goroutine或(g),它们与(m) s不同。 GO分配每个(p)的初始(m)和(g)s在(p)s之间分布。在此模型中,由于OS调度程序在CPU核心上执行线程的上下文切换,因此GO调度程序通过主机线程A.K.A(M)执行GOROUTINE的上下文切换。

Go scheduler

GO Scheduler用来执行上下文切换的窍门,例如网络轮询和异步系统调用和 Work kething 讨论。但是,结果是,在线程上io-bond goroutines的上下文切换使该线程看起来像OS调度程序的CPU结合。从操作系统的角度来看,该程序的线程(m)s总是有工作要做,即使这些作品实际上与不同的goroutines相关。由于GO调度程序的上下文切换比OS的上下文更便宜,因此GO Runtime设法将上下文开关的开销最小化,同时确保该程序在每个CPU内核上都会达到最小可能的空闲时间。因此,通过优化上面解释的权衡,GO获得了绩效。