golang GMP
2021-11-19 09:39:38 3 举报
AI智能生成
Golang GMP(Go Multiple Precision arithmetic)是一个用于处理高精度算术运算的库。它提供了一组数据类型和操作符,可以用于执行任意精度的整数和浮点数计算。GMP的主要优势在于其高效的算法和内存管理,使得在处理大量数据时能够保持较低的性能开销。 GMP支持多种编程语言,包括C、C++、Python等,但在Golang中使用时,需要通过cgo调用C语言实现的GMP库。这使得在Golang中使用GMP相对简单,只需引入相应的包即可。 总之,Golang GMP是一个功能强大且易于使用的高精度算术运算库,适用于需要在Golang中进行大量数值计算的场景。
作者其他创作
大纲/内容
以线程为调度的基本单位,利用调度算法来进行明智的决策,从而减少调度延迟
基本概念
这意味着线程已停止并等待某些事情才能继续。这可能是因为等待硬件(磁盘、网络)、操作系统(系统调用)或同步调用(原子、互斥)等原因。这些类型的延迟是性能不佳的根本原因。
Waiting
Runable
这意味着线程已放置在核心上并正在执行其机器指令。按照每个程序希望的,应用程序正在完成相关工作。
Running
线程的状态
这项工作永远不会造成线程可能处于等待状态的情况. 这是一项不断进行计算的工作
CPU密集型
这是导致线程进入等待状态的工作。这项工作包括通过网络请求访问资源或对操作系统进行系统调用。如我们访问数据库,进行同步事件(互斥体、原子),这会导致线程进入等待。
IO密集型
线程的工作方式
在CPU核心上交换线程的物理行为称为上下文切换。发生上下文切换是在当调度程序从内核中取出一个 Executing 线程并用一个 Runnable Thread 替换它时。从运行队列中选择的线程进入 Executing 状态,被取出的线程可以移回 Runnable 状态(如果它仍然具有运行能力),或进入 Waiting 状态(如果由于 IO-Bound 类型的请求而被替换)。
上下文切换资源消耗严重,因为在内核上和内核外交换线程需要时间。上下文切换过程中产生的延迟量取决于不同的因素,但花费约1000到约1500纳秒的时间。硬件在平均情况下,在每个核上每纳秒执行12条指令,上下文切换可能会花费约12k到约18k条指令的延迟。本质上,您的程序在上下问切换时正在失去大量执行指令的能力
缺点
线程的上下文切换
线程
OS
OS调度
Go 语言调度器中待执行的任务,它在运行时调度器中的地位与线程在操作系统中差不多,但是它占用了更小的内存空间,也降低了上下文切换的开销。Goroutine 只存在于 Go 语言的运行时,它是 Go 语言在用户态提供的线程,作为一种粒度更细的资源调度单元,如果使用得当能够在高并发的场景下更高效地利用机器的 CPU
概念
内存占用小 一个G 2KB左右,可以大量开辟
可以灵活调度,调度切换开销小
优势
刚刚被分配并且还没有被初始化
_Gidle
没有执行代码,没有栈的所有权,存储在运行队列中
_Grunnable
可以执行代码,拥有栈的所有权,被赋予了内核线程 M 和处理器 P
_Grunning
正在执行系统调用,拥有栈的所有权,没有执行用户代码,被赋予了内核线程 M 但是不在运行队列上
_Gsyscall
由于运行时而被阻塞,没有执行用户代码并且不在运行队列上,但是可能存在于 Channel 的等待队列上
_Gwaiting
没有被使用,没有执行代码,可能有分配的栈
_Gdead
栈正在被拷贝,没有执行代码,不在运行队列上
_Gcopystack
由于抢占而被阻塞,没有执行用户代码并且不在运行队列上,等待唤醒
_Gpreempted
GC 正在扫描栈空间,没有执行代码,可以与其他状态同时存在
_Gscan
状态
Goroutine 正在等待某些条件满足,例如:系统调用结束等,包括 _Gwaiting、_Gsyscall 和 _Gpreempted 几个状态
waiting
Goroutine 已经准备就绪,可以在线程运行,如果当前程序中有非常多的 Goroutine,每个 Goroutine 就可能会等待更多的时间,即 _Grunnable;
runnable
Goroutine 正在某个线程上运行,即 _Grunning;
running
状态简化(就跟线程一样了)
状态切换
goroutine
只包含 40 多行代码;
程序中只能存在一个活跃线程,由 G-M 模型组成
单线程调度器 0.x
允许运行多线程的程序;
全局锁导致竞争严重
多线程调度器 1.0
引入了处理器 P,构成了目前的 G-M-P 模型;
在处理器 P 的基础上实现了基于工作窃取的调度器;
在某些情况下,Goroutine 不会让出线程,进而造成饥饿问题;
时间过长的垃圾回收(Stop-the-world,STW)会导致程序长时间无法工作
任务窃取调度器 1.1
通过编译器在函数调用时插入抢占检查指令,在函数调用时检查当前 Goroutine 是否发起了抢占请求,实现基于协作的抢占式调度;
Goroutine 可能会因为垃圾回收和循环长时间占用资源导致程序暂停;(因为一直执行某个函数逻辑,不调用其它函数,就没办法触发抢占调度)
基于协作的抢占式调度器 - 1.2 ~ 1.13
实现基于信号的真抢占式调度;
垃圾回收在扫描栈时会触发抢占调度;
抢占的时间点不够多,还不能覆盖全部的边缘情况;
基于信号的抢占式调度器 - 1.14 ~ 至今
抢占式调度器 1.2-至今
对运行时的各种资源进行分区
非均匀存储访问调度器 提案
历史版本
G: Goroutine
M:Machine(与os线程一一对应)
模型结构
创建,销毁跟调度G都需要每个M获取到全局队列的锁,竞争非常激烈
G在线程种传递问题(如一个G创建出一个新的G,新G需要放到全局队列中,被其它线程获取执行,增大了调度延迟)
Per-M的内存问题,每个M都有一个cache,然而实际上只有在运行的M才需要Cache,这就导致,很多阻塞在系统调用的M产生了内存浪费
GM模型
默认情况下就是你CPU核数个P(超线程下就是2倍),可配置
数量
存放等待运行的G
最多拥有256个G
本地队列
P: processor 处理器
把mcache从M移动到P中
不再是都用全局的G队列了,每个P都有自己的G队列,新的G优先放入到自己的队列中,满了后在批量放到全局队列中,优先从自己的本地队列中获取G执行
当因为网络或者锁进行切换时,G与M分离,M通过调度执行新的G(异步阻塞)
当因为系统调用阻塞或cgo运行一段时间后,sysmon携程会将P与M分离,由其它的M与P绑定,执行P中的G
对比GM改变
当前的正在执行的G进行系统调用阻塞线程时,让P跟M解绑,把P转移给其它空闲的线程执行
hand off机制
当某个P的队列中没有可运行的G时,可以从全局队列,或者其它P中获取G执行
work-stealing
避免频繁的创建跟销毁线程,而是复用线程
复用线程
最多有GOMAPXPROCS个线程分布在多个CPU上运行
利用并行
通过抢占式调度,一个Goroutine最多运行10ms,防止其它Goroutine饿死
抢占
当M执行work-stealing从其它P偷不到G时
全局G队列
调度策略
初始时时20us,运行1ms后逐渐翻倍,最后时10ms运行一次
抢占成功后,恢复成20us
执行间隔
调度等待执行的timer,把空闲的M和P来执行timer任务
如果GC两分钟都没有运行的话,强制执行GC
系统调用,P跟M进行脱离
非系统调用,通知抢占(执行时间大于10ms的G)
执行抢占
归还内存:每5分钟归还GC后不再使用的span给操作系统(scavenge)
工作内容
监控线程sysmon
GPM模型
调度器
https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part1.html
https://zhao520a1a.github.io/2020/09/10/Go%E8%B0%83%E5%BA%A6%E7%AF%87/
https://learnku.com/articles/41728
https://www.zhihu.com/question/20862617
https://morsmachine.dk/go-scheduler
https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/
引用
go-scheduler
0 条评论
回复 删除
下一页