最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2021年06月28日 11:49:09   作者:yuchenfw  
select 是 Go 中的一個(gè)控制結(jié)構(gòu),類似于用于通信的 switch 語句。每個(gè) case 必須是一個(gè)通信操作,要么是發(fā)送要么是接收。接下來通過本文給大家介紹go select編譯期的優(yōu)化處理邏輯使用場景分析,感興趣的朋友一起看看吧

前言

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

select的編譯處理

select編譯時(shí)的核心處理邏輯如下:

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個(gè)case(單個(gè)操作)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表達(dá)式
			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個(gè)接收者
					if n.Left == nil { // 沒有接收者
						n = n.Right // 只需保留右側(cè)
					} else { // 
						n.Op = OAS // 只有一個(gè)接收者,更新Op為OAS
					}
					break
				}

				if n.Left == nil { // 檢查是否表達(dá)式或賦值
					nblank = typecheck(nblank, ctxExpr|ctxAssign)
					n.Left = nblank
				}

				n.Op = OAS2 // OSELRECV2多個(gè)接收者
				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編譯時(shí)會(huì)根據(jù)case的數(shù)量進(jìn)行優(yōu)化:

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

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

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

4.一般的case
selectgo

總結(jié)

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

select編譯優(yōu)化

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

相關(guān)文章

  • Golang在圖像中繪制矩形框的示例代碼

    Golang在圖像中繪制矩形框的示例代碼

    這篇文章主要介紹了Golang在圖像中繪制矩形框的示例代碼,文中有詳細(xì)的代碼示例供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下
    2008-08-08
  • GO語言延遲函數(shù)defer用法分析

    GO語言延遲函數(shù)defer用法分析

    這篇文章主要介紹了GO語言延遲函數(shù)defer用法,較為詳細(xì)的分析了GO語言的特性與具體用法,并給出了一個(gè)比較典型的應(yīng)用實(shí)例,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-12-12
  • Go高級(jí)特性探究之優(yōu)先級(jí)隊(duì)列詳解

    Go高級(jí)特性探究之優(yōu)先級(jí)隊(duì)列詳解

    Heap?是一種數(shù)據(jù)結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu)常用于實(shí)現(xiàn)優(yōu)先隊(duì)列,這篇文章主要就是來和大家深入探討一下GO語言中的優(yōu)先級(jí)隊(duì)列,感興趣的可以了解一下
    2023-06-06
  • 一文教你如何優(yōu)雅處理Golang中的異常

    一文教你如何優(yōu)雅處理Golang中的異常

    我們在使用Golang時(shí),不可避免會(huì)遇到異常情況的處理,與Java、Python等語言不同的是,Go中并沒有try...catch...這樣的語句塊,這個(gè)時(shí)候我們?nèi)绾尾拍芨玫奶幚懋惓D??本文來教你正確方法
    2022-11-11
  • go語言中json處理方式詳解

    go語言中json處理方式詳解

    這篇文章主要介紹了go語言中json處理方式,文中通過實(shí)例代碼講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴跟著小編一起來看看吧
    2024-05-05
  • go語言執(zhí)行windows下命令行的方法

    go語言執(zhí)行windows下命令行的方法

    這篇文章主要介紹了go語言執(zhí)行windows下命令行的方法,實(shí)例分析了Go語言操作windows下命令行的技巧,需要的朋友可以參考下
    2015-03-03
  • Go語言中并發(fā)的工作原理

    Go語言中并發(fā)的工作原理

    本文詳細(xì)講解了Go語言中并發(fā)的工作原理,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Go語言中通過Lua腳本操作Redis的方法

    Go語言中通過Lua腳本操作Redis的方法

    這篇文章主要給大家介紹了關(guān)于Go語言中通過Lua腳本操作Redis的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • GO語言獲取系統(tǒng)環(huán)境變量的方法

    GO語言獲取系統(tǒng)環(huán)境變量的方法

    這篇文章主要介紹了GO語言獲取系統(tǒng)環(huán)境變量的方法,實(shí)例分析了Getenv方法操作環(huán)境變量的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • GO?env環(huán)境變量配置命令使用

    GO?env環(huán)境變量配置命令使用

    這篇文章主要為大家介紹了GO?env環(huán)境變量配置命令使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05

最新評(píng)論

江川县| 土默特右旗| 新巴尔虎右旗| 丰顺县| 宁国市| 咸丰县| 大新县| 天镇县| 镶黄旗| 和林格尔县| 徐州市| 玉门市| 漳州市| 洛隆县| 株洲市| 大田县| 泾源县| 南江县| 宁蒗| 罗山县| 明水县| 石阡县| 迁安市| 遂宁市| 泸西县| 英吉沙县| 乐亭县| 和龙市| 光山县| 郑州市| 榕江县| 扶沟县| 兴国县| 和田县| 灵川县| 大关县| 蒙阴县| 卢湾区| 蒲城县| 麻江县| 兴安盟|