首页 > 其他分享 >golang io优化

golang io优化

时间:2024-02-24 20:44:48浏览次数:20  
标签:调用 多路复用 epoll Goroutine golang fd io runtime 优化

如果想兼顾开发效率,又能保证高并发,协程就是最好的选择。它可以在保持异步化运行机制的同时,用同步方式写代码(goroutine-per-connection),这在实现高并发的同时,缩短了开发周期,是高性能服务未来的发展方向。

  1. CPU 和 IO 设备是不同的设备,能并行运行。合理调度程序,充分利用硬件,就能跑出很好的性能;
  2. Go 的 IO 最最核心的是 io 库,除了定义 interface (Reader/Writer),还实现了通用的函数,比如 Copy 之类的;
  3. 内存字节数组可以作为 Reader ,Writer ,实现在 bytes 库中,字符串可以作为 Reader,实现在 strings 库中,strings.NewReader;网络句柄可以作为 Reader ,Writer ,实现在 net 库中,net.Conn;文件句柄可以作为 Reader ,Writer ,实现在 os 库中,os.File ;

整体理念

从tcp socket诞生后,网络编程架构模型也几经演化,大致是:“每进程一个连接” –> “每线程一个连接” –> “Non-Block + I/O多路复用(linux epoll/windows iocp/freebsd darwin kqueue/solaris Event Port)”。伴随着模型的演化,服务程序愈加强大,可以支持更多的连接,获得更好的处理性能。不过I/O多路复用也给使用者带来了不小的复杂度,以至于后续出现了许多高性能的I/O多路复用框架, 比如libevent、libev、libuv等,以帮助开发者简化开发复杂性,降低心智负担。不过Go的设计者似乎认为I/O多路复用的这种通过回调机制割裂控制流的方式依旧复杂,且有悖于“一般逻辑”设计,为此Go语言将该“复杂性”隐藏在Runtime中了:Go开发者无需关注socket是否是 non-block的,也无需亲自注册文件描述符的回调,只需在每个连接对应的goroutine中以“block I/O”的方式对待socket处理即可。

原理

  1. 多路复用 有赖于 linux 的epoll 机制,具体的说 是 epoll_create/epoll_ctl/epoll_wait 三个函数
  2. epoll 机制包含 两个fd: epfd 和 待读写数据的fd(比如socket)。先创建efpd,然后向epfd 注册fd事件, 之后触发epoll_wait 轮询注册在epfd 的fd 事件发生了没有。
  3. netpoller 负责将 操作系统 提供的nio 转换为 goroutine 支持的blocking io。为屏蔽linux、windows 等底层nio 接口的差异,netpoller 定义一个 虚拟接口来封装底层接口。
     func netpollinit()
     func netpollopen(fd uintptr, pd *pollDesc) int32
     func netpoll(delta int64) gList
     func netpollBreak()
     func netpollIsPollDescriptor(fd uintptr) bool
    

 netpoller 基于 linux 的epoll 接口 的实现 

Goroutine 让出线程并等待读写事件:当我们在文件描述符上执行读写操作时,如果文件描述符不可读或者不可写,当前 Goroutine 就会执行 runtime.poll_runtime_pollWait 检查 runtime.pollDesc 的状态并调用 runtime.netpollblock 等待文件描述符的可读或者可写。runtime.netpollblock会使用运行时提供的 runtime.gopark 让出当前线程,将 Goroutine 转换到休眠状态并等待运行时的唤醒。

I/O 多路复用需要使用特定的系统调用/select,java 语言需要显式调用select ,而golang 则通过netpoller 组件将select调用 重新隐藏了。

多路复用等待读写事件的发生并返回:netpoller并不是由runtime中的某一个线程独立运行的,runtime中的调度和系统调用会通过 runtime.netpoll 与网络轮询器交换消息,获取待执行的 Goroutine 列表,恢复Goroutine 为运行状态,并将待执行的 Goroutine 加入运行队列等待处理。

io 前后的GPM

G1 正在 M 上执行,还有 3 个 Goroutine 在 LRQ 上等待执行。网络轮询器空闲着,什么都没干。

G1 想要进行网络系统调用,因此它被移动到网络轮询器并且处理异步网络系统调用。然后,M 可以从LRQ 执行另外的 Goroutine。此时,G2 就被上下文切换到 M 上了。

异步网络系统调用由网络轮询器完成,G1 被移回到 P 的 LRQ 中。一旦 G1 可以在 M 上进行上下文切换,它负责的 Go 相关代码就可以再次执行。

执行网络系统调用不需要额外的 M。网络轮询器使用系统线程,它时刻处理一个有效的事件循环/eventloop。

带缓存的网络 I/O

每次从 net.Conn 尝试读取其内部缓存大小的数据,而不是用户传入的希望读取的数据大小。这些数据缓存在内存中,这样,后续的 Read 就可以直接从内存中得到数据,而不是每次都要从 net.Conn 读取,从而降低 Syscall 调用的频率。 有一个 生产-消费 []byte 过程:netpoller 协程 从connection/socket 读[]byte 到buffer,业务协程 从buffer 中读取[]byte decode 处理。这个“队列” 如何实现?

RingBuffer 天然合适,但是RingBuffer 存满后需要扩容,扩容需要copy,copy 会造成data race(read/write 指针不能碰面)。再进一步,如何实现一个无锁的ring buffer?使用链表解决扩容问题;使用sync.Pool 复用链表节点;维护一个length字段,通过atomic 避免data race。

重用内存对象

go tool pprof 可以观测占用内存最多的函数 和 代码(哪一行)。比如 每次服务端收到一个客户端 submit 请求时,都会在堆上分配一块内存表示 Submit 类型的实例

s := Submit{}
// 改为
var SubmitPool = sync.Pool{
    New: func() interface{} {
        return &Submit{}
    },
}
s := SubmitPool.Get().(*Submit) // 从SubmitPool池中获取一个Submit内存对象
...
SubmitPool.Put(submit)          // 将submit对象归还给Pool池

标签:调用,多路复用,epoll,Goroutine,golang,fd,io,runtime,优化
From: https://www.cnblogs.com/muzinan110/p/18031536

相关文章

  • golang性能优化
    性能优化流程理清待优化代码的常用逻辑与场景根据实际场景编写压测用例使用pprof或者火焰图等工具取得数据找到热点代码重点优化Profilingpprof是用于可视化和分析性能分析数据的工具。为什么pprof可以帮助我们分析Go程序性能呢?因为它可以采集程序运行时数据:比如说协程栈,这样服......
  • Flutter 和 RN 的iOS 打包部署
    前沿现在科技的发展,现在多端适配已经成为趋势, 关于打包和发布的文字描述进入项目目录:在终端中切换到ReactNative或者Flutter项目的根目录。打开Xcode项目:打开Xcode并在菜单中选择File->Open,然后选择ReactNative项目中的.xcworkspace文件。步骤2:配置Build......
  • EvolveGCN Evolving Graph Convolutional Networks for Dynamic Graphs
    目录概符号说明EvolveGCN代码ParejaA.,DomeniconiG.,ChenJ.,MaT.,SuzumuraT.,KanezashiH.,KalerT.,SchardlT.B.andLeisersonC.E.EvolveGCN:Evolvinggraphconvolutionalnetworksfordynamicgraphs.AAAI,2019.概GCN用在动态图上的早期探索.符号......
  • Golang Swag 注释
    常用的注释用法:@title:指定API的标题。@description:对API的简要描述。@version:API的版本号。@termsOfService:API的使用条款。@contact:API的联系方式,例如邮箱。@license:API的许可证信息。@host:API的主机名和端口号。@BasePath:API的基本路径。@S......
  • session验证与jwt验证
    简单谈谈session验证和jwt验证session验证简化版原理1.用户rds登录login页面,服务器向用户端发送一个sessionid,同时服务器创建一个和这个sessionid相关的会话信息(用于该用户下次session验证)2.用户再次登录或者访问授权页面,将sessionid发送到服务器3.服务器通过保留的会话信......
  • Educational Codeforces Round 162 (Rated for Div. 2)
    不会F的场。A答案是最左的\(1\)和最右的\(1\)之间的\(0\)的个数。Code#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;intmain(){ ios::sync_with_stdio(false); cin.tie(0); intt; cin>>t; while(t--){ intn; cin>>n......
  • VMware Workstation 安装Ubuntu虚拟机 屏幕窗口分辨率 自动调整大小 自动适应客户机
    Ubuntu18.04.5LTSVMwareWorkstation16Pro 首先排查了vmwaretools的安装问题首先尝试通过这样安装 点击安装后,好像是有个cd挂载上,复制这个文件到桌面解压这个压缩包,在文件夹打开终端sudo./vmware-install.pl全按回车应该就可以其间Theinstallerhasdetect......
  • Semantic Kernel 学习笔记:体验基于 prompt function 实现的 Plugin
    在一个SemanticKernelplugin中可以创建两种类型的function,分别是nativefunction与promptfunction(之前叫semanticfunction)。下面这款plugin中给C#method添加了[KernelFunction]attribute,就是nativefunctionpublicclassLightPlugin{publicboolIsOn......
  • 拉取github仓库报错 Authentication failed for..
    remote:Pleaseseehttps://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/formoreinformation.(大家也可以看一下,是github要求使用双因子验证的通知)二、解决步骤1、首先进入GitHub,点击头像打开下拉栏,进入settings页面。2、点击进入下面的de......
  • Educational Codeforces Round 162 [Rated for Div. 2]
    第二次,三道题,剩下的回学校再补,总结:还是需要多练习A:让数列里所有的1都挨在一起,可以看出,相邻1之间有多少个0就需要操作几次,特判:当只有一个1或者原本全是1就输出0;voidsolve(){cin>>n;inttop1=0,top2=0,cnt=0;memset(a,0,sizeof(a));memset(b,0,siz......