go select编译期的优化处理逻辑使用场景分析
前言
select
作为Go
chan
通信的重要监听工具,有着很广泛的使用场景。select
的使用主要是搭配通信case使用,表面上看,只是简单的select
及case
搭配,实际上根据case
的数量及类型,在编译时select
会进行优化处理,根据不同的情况调用不同的底层逻辑。
select的编译处理
select
编译时的核心处理逻辑如下:
func walkselectcases(cases *Nodes) []*Node { ncas := cases.Len() sellineno := lineno // optimization: zero-case select // 针对没有case的select优化 if ncas == 0 { return []*Node{mkcall("block", nil, nil)} } // optimization: one-case select: single op. // 针对1个case(单个操作)select的优化 if ncas == 1 { cas := cases.First() setlineno(cas) l := cas.Ninit.Slice() if cas.Left != nil { // not default: 非default case n := cas.Left // 获取case表达式 l = append(l, n.Ninit.Slice()...) n.Ninit.Set(nil) switch n.Op { default: Fatalf("select %v", n.Op) case OSEND: // Left <- Right // already ok // n中已包含left/right case OSELRECV, OSELRECV2: // OSELRECV(Left = <-Right.Left) OSELRECV2(List = <-Right.Left) if n.Op == OSELRECV || n.List.Len() == 0 { // 左侧有0或1个接收者 if n.Left == nil { // 没有接收者 n = n.Right // 只需保留右侧 } else { // n.Op = OAS // 只有一个接收者,更新Op为OAS } break } if n.Left == nil { // 检查是否表达式或赋值 nblank = typecheck(nblank, ctxExpr|ctxAssign) n.Left = nblank } n.Op = OAS2 // OSELRECV2多个接收者 n.List.Prepend(n.Left) // 将left放在前面 n.Rlist.Set1(n.Right) n.Right = nil n.Left = nil n.SetTypecheck(0) n = typecheck(n, ctxStmt) } l = append(l, n) } l = append(l, cas.Nbody.Slice()...) // case内的处理 l = append(l, nod(OBREAK, nil, nil)) // 添加break return l } // convert case value arguments to addresses. // this rewrite is used by both the general code and the next optimization. var dflt *Node for _, cas := range cases.Slice() { setlineno(cas) n := cas.Left if n == nil { dflt = cas continue } switch n.Op { case OSEND: n.Right = nod(OADDR, n.Right, nil) n.Right = typecheck(n.Right, ctxExpr) case OSELRECV, OSELRECV2: if n.Op == OSELRECV2 && n.List.Len() == 0 { n.Op = OSELRECV } if n.Left != nil { n.Left = nod(OADDR, n.Left, nil) n.Left = typecheck(n.Left, ctxExpr) } } } // optimization: two-case select but one is default: single non-blocking op. if ncas == 2 && dflt != nil { cas := cases.First() if cas == dflt { cas = cases.Second() } n := cas.Left setlineno(n) r := nod(OIF, nil, nil) r.Ninit.Set(cas.Ninit.Slice()) switch n.Op { default: Fatalf("select %v", n.Op) case OSEND: // if selectnbsend(c, v) { body } else { default body } ch := n.Left r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right) case OSELRECV: // if selectnbrecv(&v, c) { body } else { default body } ch := n.Right.Left elem := n.Left if elem == nil { elem = nodnil() } r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch) case OSELRECV2: // if selectnbrecv2(&v, &received, c) { body } else { default body } ch := n.Right.Left elem := n.Left if elem == nil { elem = nodnil() } receivedp := nod(OADDR, n.List.First(), nil) receivedp = typecheck(receivedp, ctxExpr) r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch) } r.Left = typecheck(r.Left, ctxExpr) r.Nbody.Set(cas.Nbody.Slice()) r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...)) return []*Node{r, nod(OBREAK, nil, nil)} } if dflt != nil { ncas-- } casorder := make([]*Node, ncas) nsends, nrecvs := 0, 0 var init []*Node // generate sel-struct lineno = sellineno selv := temp(types.NewArray(scasetype(), int64(ncas))) r := nod(OAS, selv, nil) r = typecheck(r, ctxStmt) init = append(init, r) // No initialization for order; runtime.selectgo is responsible for that. order := temp(types.NewArray(types.Types[TUINT16], 2*int64(ncas))) var pc0, pcs *Node if flag_race { pcs = temp(types.NewArray(types.Types[TUINTPTR], int64(ncas))) pc0 = typecheck(nod(OADDR, nod(OINDEX, pcs, nodintconst(0)), nil), ctxExpr) } else { pc0 = nodnil() } // register cases for _, cas := range cases.Slice() { setlineno(cas) init = append(init, cas.Ninit.Slice()...) cas.Ninit.Set(nil) n := cas.Left if n == nil { // default: continue } var i int var c, elem *Node switch n.Op { default: Fatalf("select %v", n.Op) case OSEND: i = nsends nsends++ c = n.Left elem = n.Right case OSELRECV, OSELRECV2: nrecvs++ i = ncas - nrecvs c = n.Right.Left elem = n.Left } casorder[i] = cas setField := func(f string, val *Node) { r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val) r = typecheck(r, ctxStmt) init = append(init, r) } c = convnop(c, types.Types[TUNSAFEPTR]) setField("c", c) if elem != nil { elem = convnop(elem, types.Types[TUNSAFEPTR]) setField("elem", elem) } // TODO(mdempsky): There should be a cleaner way to // handle this. if flag_race { r = mkcall("selectsetpc", nil, nil, nod(OADDR, nod(OINDEX, pcs, nodintconst(int64(i))), nil)) init = append(init, r) } } if nsends+nrecvs != ncas { Fatalf("walkselectcases: miscount: %v + %v != %v", nsends, nrecvs, ncas) } // run the select lineno = sellineno chosen := temp(types.Types[TINT]) recvOK := temp(types.Types[TBOOL]) r = nod(OAS2, nil, nil) r.List.Set2(chosen, recvOK) fn := syslook("selectgo") r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, nodintconst(int64(nsends)), nodintconst(int64(nrecvs)), nodbool(dflt == nil))) r = typecheck(r, ctxStmt) init = append(init, r) // selv and order are no longer alive after selectgo. init = append(init, nod(OVARKILL, selv, nil)) init = append(init, nod(OVARKILL, order, nil)) if flag_race { init = append(init, nod(OVARKILL, pcs, nil)) } // dispatch cases dispatch := func(cond, cas *Node) { cond = typecheck(cond, ctxExpr) cond = defaultlit(cond, nil) r := nod(OIF, cond, nil) if n := cas.Left; n != nil && n.Op == OSELRECV2 { x := nod(OAS, n.List.First(), recvOK) x = typecheck(x, ctxStmt) r.Nbody.Append(x) } r.Nbody.AppendNodes(&cas.Nbody) r.Nbody.Append(nod(OBREAK, nil, nil)) init = append(init, r) } if dflt != nil { setlineno(dflt) dispatch(nod(OLT, chosen, nodintconst(0)), dflt) } for i, cas := range casorder { setlineno(cas) dispatch(nod(OEQ, chosen, nodintconst(int64(i))), cas) } return init }
select
编译时会根据case的数量进行优化:
1.没有case
直接调用block
2.1个case
(1)default
case,直接执行body
(2) send
/recv
case (block
为true),按照单独执行的结果确认,可能会发生block
(3) send
调用对应的chansend1
(4) recv
调用对应的chanrecv1/chanrecv2
3.2个case且包含一个default case
(1) send
/recv
case (block为false),按照单独执行的结果确认case是否ok,!ok则执行default case,不会发生block
(2) send
调用对应的selectnbsend
(3) recv
调用对应的selectnbrecv
/selectnbrecv2
4.一般的case
selectgo
总结
最后,以一张图进行简单总结。
以上就是go select编译期的优化处理逻辑使用场景分析的详细内容,更多关于go select编译的资料请关注猪先飞其它相关文章!
相关文章
- 这篇文章主要介绍了Mybatis Plus select 实现只查询部分字段的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-01
- 在程序员中,尤其是go新手,经常听到的一个讨论话题是:如何处理错误,这篇文章主要给大家介绍了关于Go应用中优雅处理Error的一些相关技巧,需要的朋友可以参考下...2021-09-08
- 这篇文章主要介绍了vue 监听 Treeselect 选择项的改变操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-01
- 1.在没有设置默认值的情况下: 复制代码 代码如下:SELECT userinfo.id, user_name, role, adm_regionid, region_name , create_timeFROM userinfoLEFT JOIN region ON userinfo.adm_regionid = region.id 结果:...2014-05-31
- 这篇文章主要介绍了vue treeselect获取当前选中项的label实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-01
vue Treeselect下拉树只能选择第N级元素实现代码
这篇文章主要介绍了vue Treeselect下拉树只能选择第N级元素实现代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-01- 这篇文章主要介绍了Vue select 绑定动态变量的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-23
Django def clean()函数对表单中的数据进行验证操作
这篇文章主要介绍了Django def clean()函数对表单中的数据进行验证操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-09MySQL中SELECT+UPDATE处理并发更新问题解决方案分享
问题背景: 假设MySQL数据库有一张会员表vip_member(InnoDB表),结构如下: 当一个会员想续买会员(只能续买1个月、3个月或6个月)时,必须满足以下业务要求: •如果end_at早于当前时间,则设置start_at为当前时间,end_at为当前时...2014-05-31- 下面小编就为大家带来一篇jQuery为动态生成的select元素添加事件的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-09-01
- 这篇文章主要介绍了VSCode C++多文件编译的简单使用方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
- 这篇文章主要介绍javascript获取select标签选中的值方法,比较实用,需要的朋友可以参考下。...2016-06-12
- 今天将从 Vue 的入口文件开始,看看声明了一个 Vue 的单文件之后是如何被 compile-core 编译核心模块编译成渲染函数的。下面小编讲解并附上代码分析展现在文章里,感兴趣的小伙伴不要错过奥...2021-09-25
- 这篇文章主要为大家详细介绍了js实现可输入可选择的select下拉框,可及时匹配包含输入的内容,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-01-09
Mybatis之Select Count(*)的获取返回int的值操作
这篇文章主要介绍了Mybatis之Select Count(*)的获取返回int的值操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-23- 这篇文章主要介绍了golang官方嵌入文件到可执行程序,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-20
- 这篇文章主要介绍了go浮点数转字符串保留小数点后N位解决办法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-05-11
- 这篇文章主要为大家详细介绍了jquery Ajax实现Select动态添加数据的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-06-15
- 这篇文章主要介绍了Select下拉框模糊查询功能实现代码的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下...2016-07-29
- 这篇文章主要介绍了Go语言使用读写OPC详解,图文讲解的很清晰,有感兴趣的同学可以学习下...2021-03-05