go select编译期的优化处理逻辑使用场景分析
作者:yuchenfw 时间:2024-05-25 15:16:43
前言
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.一般的caseselectgo
来源:https://blog.csdn.net/xz_studying/article/details/118280886
标签:go,select,编译
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
Python高级特性 切片 迭代解析
2022-03-28 14:32:49
![](https://img.aspxhome.com/file/2023/5/84915_0s.png)
Tensorflow: 从checkpoint文件中读取tensor方式
2022-07-20 11:39:29
用Python Turtle画棵樱花树送给自己
2022-06-30 10:16:47
![](https://img.aspxhome.com/file/2023/8/88968_0s.jpg)
MySQL批量更新的四种方式总结
2024-01-13 17:53:08
CTF中的PHP特性函数解析之中篇
2023-06-11 12:56:20
![](https://img.aspxhome.com/file/2023/7/55517_0s.jpg)
Mysql json类型字段Java+Mybatis数据字典功能的实践方式
2024-01-22 00:24:34
![](https://img.aspxhome.com/file/2023/3/71243_0s.jpg)
Python Flask框架实现简单加法工具过程解析
2021-08-17 20:36:11
![](https://img.aspxhome.com/file/2023/4/66864_0s.png)
ORACLE11g随RHEL5系统自动启动与关闭的设置方法
2009-08-31 12:43:00
《写给大家看的设计书》阅读笔记之对比原则
2009-07-15 10:14:00
![](https://img.aspxhome.com/file/UploadPic/20097/15/dq1-53s.jpg)
vue百度地图 + 定位的详解
2024-06-07 15:20:37
![](https://img.aspxhome.com/file/2023/0/123020_0s.png)
python模拟登录并且保持cookie的方法详解
2023-09-06 06:07:16
python一行输入n个数据问题
2023-09-11 21:50:48
![](https://img.aspxhome.com/file/2023/5/78715_0s.png)
5招带你轻松优化MySQL count(*)查询性能
2024-01-28 04:40:58
![](https://img.aspxhome.com/file/2023/0/107430_0s.png)
改进评论提交表单
2009-03-25 20:37:00
![](https://img.aspxhome.com/file/UploadPic/20093/25/usabilitypost-96s.png)
ASP常见的保留字整理(变量与表名注意不能用)
2013-06-01 19:58:01
python中%格式表达式实例用法
2022-02-05 06:59:02
数据库分页大全(mssql,mysql,oracle)
2010-10-25 20:02:00
Pandas对每个分组应用apply函数的实现
2022-01-26 04:02:00
![](https://img.aspxhome.com/file/2023/5/78775_0s.jpg)
一文教会你用Python读取PDF文件
2021-03-23 00:24:55
![](https://img.aspxhome.com/file/2023/6/102896_0s.png)
python实现windows壁纸定期更换功能
2022-09-06 15:05:11
![](https://img.aspxhome.com/file/2023/9/109389_0s.jpg)