佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > go select編譯期的優(yōu)化處理邏輯使用場景分析

go select編譯期的優(yōu)化處理邏輯使用場景分析

熱門標(biāo)簽:AI電銷 地方門戶網(wǎng)站 呼叫中心市場需求 鐵路電話系統(tǒng) 百度競價排名 Linux服務(wù)器 網(wǎng)站排名優(yōu)化 服務(wù)外包

前言

select作為Go chan通信的重要監(jiān)聽工具,有著很廣泛的使用場景。select的使用主要是搭配通信case使用,表面上看,只是簡單的selectcase搭配,實際上根據(jù)case的數(shù)量及類型,在編譯時select會進行優(yōu)化處理,根據(jù)不同的情況調(diào)用不同的底層邏輯。

select的編譯處理

select編譯時的核心處理邏輯如下:

func walkselectcases(cases *Nodes) []*Node {
	ncas := cases.Len()
	sellineno := lineno

	// optimization: zero-case select
	// 針對沒有case的select優(yōu)化
	if ncas == 0 {
		return []*Node{mkcall("block", nil, nil)}
	}

	// optimization: one-case select: single op.
	// 針對1個case(單個操作)select的優(yōu)化
	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 { // 左側(cè)有0或1個接收者
					if n.Left == nil { // 沒有接收者
						n = n.Right // 只需保留右側(cè)
					} 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內(nèi)的處理
		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編譯時會根據(jù)case的數(shù)量進行優(yōu)化:

1.沒有case
直接調(diào)用block

2.1個case
(1)default case,直接執(zhí)行body
(2) send/recv case (block為true),按照單獨執(zhí)行的結(jié)果確認(rèn),可能會發(fā)生block
(3) send調(diào)用對應(yīng)的chansend1
(4) recv調(diào)用對應(yīng)的chanrecv1/chanrecv2

3.2個case且包含一個default case
(1) send/recv case (block為false),按照單獨執(zhí)行的結(jié)果確認(rèn)case是否ok,!ok則執(zhí)行default case,不會發(fā)生block
(2) send調(diào)用對應(yīng)的selectnbsend
(3) recv調(diào)用對應(yīng)的selectnbrecv/selectnbrecv2

4.一般的case
selectgo

總結(jié)

最后,以一張圖進行簡單總結(jié)。

以上就是go select編譯期的優(yōu)化處理邏輯使用場景分析的詳細(xì)內(nèi)容,更多關(guān)于go select編譯的資料請關(guān)注腳本之家其它相關(guān)文章!

您可能感興趣的文章:
  • 詳解Golang并發(fā)操作中常見的死鎖情形
  • Go 語言中的死鎖問題解決
  • Go語言死鎖與goroutine泄露問題的解決
  • golang coroutine 的等待與死鎖用法
  • Django實現(xiàn)jquery select2帶搜索的下拉框
  • Go語言使用select{}阻塞main函數(shù)介紹
  • matplotlib之多邊形選區(qū)(PolygonSelector)的使用
  • golang中的select關(guān)鍵字用法總結(jié)
  • Go select 死鎖的一個細(xì)節(jié)

標(biāo)簽:湖南 蘭州 衡水 銅川 湘潭 崇左 仙桃 黃山

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《go select編譯期的優(yōu)化處理邏輯使用場景分析》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    砀山县| 龙江县| 扶沟县| 云林县| 牟定县| 那坡县| 来宾市| 海门市| 荃湾区| 云阳县| 鲁山县| 敖汉旗| 阳西县| 彰武县| 鄂州市| 什邡市| 太湖县| 雅江县| 绥江县| 木里| 西乡县| 西充县| 弥勒县| 会东县| 丘北县| 水城县| 东方市| 信阳市| 弥勒县| 兴仁县| 东明县| 新绛县| 澄城县| 浦北县| 涡阳县| 陆良县| 康平县| 姚安县| 广宗县| 高安市| 喀什市|