青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Fork me on GitHub
隨筆 - 215  文章 - 13  trackbacks - 0
<2017年5月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910


專注即時通訊及網游服務端編程
------------------------------------
Openresty 官方模塊
Openresty 標準模塊(Opm)
Openresty 三方模塊
------------------------------------
本博收藏大部分文章為轉載,并在文章開頭給出了原文出處,如有再轉,敬請保留相關信息,這是大家對原創作者勞動成果的自覺尊重!!如為您帶來不便,請于本博下留言,謝謝配合。

常用鏈接

留言簿(1)

隨筆分類

隨筆檔案

相冊

Awesome

Blog

Book

GitHub

Link

搜索

  •  

積分與排名

  • 積分 - 220943
  • 排名 - 117

最新評論

閱讀排行榜

作者:達達
鏈接:https://zhuanlan.zhihu.com/p/20010926
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

今天我要教大家一些無用技能,也可以叫它奇技淫巧或者黑魔法。用得好可以提升性能,用得不好就會招來惡魔,嘿嘿。

黑魔法導論

為了讓大家在學習了基礎黑魔法之后能有所悟,在必要的時候能創造出本文傳授之外的屬于自己的魔法,這里需要先給大家打好基礎。

學習Go語言黑魔法之前,需要先看清Go世界的本質,你才能獲得像Neo一樣的能力。

在Go語言中,Slice本質是什么呢?是一個reflect.SliceHeader結構體和這個結構體中Data字段所指向的內存。String本質是什么呢?是一個reflect.StringHeader結構體和這個結構體所指向的內存。

在Go語言中,指針的本質是什么呢?是unsafe.Pointer和uintptr。

當你清楚了它們的本質之后,你就可以隨意的玩弄它們,嘿嘿嘿。

第一式 - 獲得Slice和String的內存數據

讓我小試身手,你有一個CGO接口要調用,需要你把一個字符串數據或者字節數組數據從Go這邊傳遞到C那邊,比如像這個:mysql/conn.go at master · funny/mysql · GitHub

查了各種教程和文檔,它們都告訴你要用C.GoString或C.GoBytes來轉換數據。

但是,當你調用這兩個函數的時候,發生了什么事情呢?這時候Go復制了一份數據,然后再把新數據的地址傳給C,因為Go不想冒任何風險。

你的C程序只是想一次性的用一下這些數據,也不得不做一次數據復制,這對于一個性能癖來說是多麼可怕的一個事實!

這時候我們就需要一個黑魔法,來做到不拷貝數據又能把指針地址傳遞給C。

// returns &s[0], which is not allowed in go
func stringPointer(s string) unsafe.Pointer {
	p := (*reflect.StringHeader)(unsafe.Pointer(&s))
	return unsafe.Pointer(p.Data)
}

// returns &b[0], which is not allowed in go
func bytePointer(b []byte) unsafe.Pointer {
	p := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	return unsafe.Pointer(p.Data)
}

以上就是黑魔法第一式,我們先去到Go字符串的指針,它本質上是一個*reflect.StringHeader,但是Go告訴我們這是一個*string,我們告訴Go它同時也是一個unsafe.Pointer,Go說好吧它是,于是你得到了unsafe.Pointer,接著你就躲過了Go的監視,偷偷的把unsafe.Pointer轉成了*reflect.StringHeader。

有了*reflect.StringHeader,你很快就取到了Data字段指向的內存地址,它就是Go保護著不想給你看到的隱秘所在,你把這個地址偷偷告訴給了C,于是C就愉快的偷看了Go的隱私。

第二式 - 把[]byte轉成string

你肯定要笑,要把[]byte轉成string還不簡單?Go語言初學者都會的類型轉換語法:string(b)。

但是你知道這么做的代價嗎?既然我們能隨意的玩弄SliceHeader和StringHeader,為什么我們不能造個string給Go呢?Go的內部會不會就是這么做的呢?

先上個實驗吧:

package labs28

import "testing"
import "unsafe"

func Test_ByteString(t *testing.T) {
	var x = []byte("Hello World!")
	var y = *(*string)(unsafe.Pointer(&x))
	var z = string(x)

	if y != z {
		t.Fail()
	}
}

func Benchmark_Normal(b *testing.B) {
	var x = []byte("Hello World!")
	for i := 0; i < b.N; i ++ {
		_ = string(x)
	}
}

func Benchmark_ByteString(b *testing.B) {
	var x = []byte("Hello World!")
	for i := 0; i < b.N; i ++ {
		_ = *(*string)(unsafe.Pointer(&x))
	}
}

這個實驗先證明了我們可以用[]byte的數據造個string給Go。接著做了兩組Benchmark,分別測試了普通的類型轉換和偽造string的效率。

結果如下:

$ go test -bench="."
PASS
Benchmark_Normal    20000000            63.4 ns/op
Benchmark_ByteString    2000000000           0.55 ns/op
ok      github.com/idada/go-labs/labs28 2.486s

喲西,顯然Go這次又為了穩定性做了些復制數據之類的事情了!這讓性能癖怎么能忍受!

我現在手頭有個[]byte,但是我想用strconv.Atoi()把它轉成字面含義對應的整數值,竟然需要發生一次數據拷貝把它轉成string,比如像這樣:mysql/types.go at master · funny/mysql · GitHub,這實在不能忍??!

出招:

// convert b to string without copy
func byteString(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

我們取到[]byte的指針,這次Go又告訴你它是*byte不是*string,你告訴它滾犢子這是unsafe.Pointer,Go這下又老實了,接著你很自在的把*byte轉成了*string,因為你知道reflect.StringHeader和reflect.SliceHeader的結構體只相差末尾一個字段,兩者的內存是對齊的,沒必要再取Data字段了,直接轉吧。

于是,世界終于安寧了,嘿嘿。

第三式 - 結構體和[]byte互轉

有一天,你想把一個簡單的結構體轉成二進制數據保存起來,這時候你想到了encoding/gob和encoding/json,做了一下性能測試,你想到效率有沒有可能更高點?

于是你又試了encoding/binady,性能也還可以,但是你還不滿意。但是瓶頸在哪里呢?你恍然大悟,最高效的辦法就是完全不解析數據也不產生數據??!

怎么做?是時候使用這個黑魔法了:

type MyStruct struct {
	A int
	B int
}

var sizeOfMyStruct = int(unsafe.Sizeof(MyStruct{}))

func MyStructToBytes(s *MyStruct) []byte {
	var x reflect.SliceHeader
	x.Len = sizeOfMyStruct
	x.Cap = sizeOfMyStruct
	x.Data = uintptr(unsafe.Pointer(s))
	return *(*[]byte)(unsafe.Pointer(&x))
}

func BytesToMyStruct(b []byte) *MyStruct {
	return (*MyStruct)(unsafe.Pointer(
		(*reflect.SliceHeader)(unsafe.Pointer(&b)).Data,
	))
}

這是個曲折但又熟悉的故事。你造了一個SliceHeader,想把它的Data字段指向你的結構體,但是Go又告訴你不可以,你像往常那樣把Go提到一邊,你得到了unsafe.Pointer,但是這次Go有不死心,它告訴你Data是uintptr,unsafe.Pointer不是uintptr,你大腳把它踢開,怒吼道:unsafe.Pointer就是uintptr,你少拿這些概念糊弄我,Go屁顛屁顛的跑開了,現在你一馬平川的來到了函數的出口,Go竟然已經在哪里等著你了!你上前三下五除二把它踢得遠遠的,順利的把手頭的SliceHeader轉成了[]byte。

過了一陣子,你拿到了一個[]byte,你知道需要把它轉成MyStruct來讀取其中的數據。Go這時候已經完全不是你的對手了,它已經洗好屁股在函數入口等你,你一行代碼就解決了它。

第四式 - 用CGO優化GC

你已經是Go世界的Neo,Go跟本沒辦法拿你怎么樣。但是有一天Go的GC突然抽風了,原來這貨是不管對象怎么用的,每次GC都給來一遍人口普查,導致系統暫停時間很長。

可是你是個性能癖,你把一堆數據都放在內存里方便快速訪問,你這時候很想再踢Go的屁股,但是你沒辦法,畢竟你還在Go的世界里,你現在得替它擦屁股了,你似乎看到Go躲在一旁偷笑。

你想到你手頭有CGO,可以輕易的用C申請到Go世界外的內存,Go的GC不會掃描這部分內存。

你還想到你可以用unsafe.Pointer將C的指針轉成Go的結構體指針。于是一大批常駐內存對象被你用這種方式轉成了Go世界的黑戶,Go的GC一下子輕松了下來。

但是你手頭還有很多Slice,于是你就利用C申請內存給SliceHeader來構造自己的Slice,于是你旗下的Slice紛紛轉成了Go世界的黑戶,Go的GC終于平靜了。

但好景總是不長久,有一天Go世界突然崩潰了,只留下一句話:Segmentation Fault。你一下慫了,怎么段錯誤了?

經過一個通宵排查,你發現你管轄的黑戶對象竟然偷偷的跟Go世界的其它合法居民搞在一起,當Go世界以為某個居民已經消亡時,用GC回收了它的住所,但是你的地下世界卻認為它還活著,還繼續訪問它。

于是你廢了一番功夫斬斷了所有關聯,世界暫時寧靜了下來。

但是你已經很累了,這時候你想起一句話:

為無為,則無不治
posted on 2017-05-04 10:35 思月行云 閱讀(182) 評論(0)  編輯 收藏 引用 所屬分類: Golang
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美一区二区日韩| 亚洲第一精品在线| 性做久久久久久免费观看欧美| 在线日韩日本国产亚洲| 免费看的黄色欧美网站| 性欧美18~19sex高清播放| 日韩一二三在线视频播| 欧美激情bt| 一区二区三区视频观看| 1204国产成人精品视频| 激情小说亚洲一区| 永久免费毛片在线播放不卡| 国产在线观看一区| 红杏aⅴ成人免费视频| 狠狠干综合网| 国产精品久久午夜| 欧美激情视频网站| 亚洲高清视频一区| 亚洲丰满在线| 亚洲精品日韩综合观看成人91| 欧美 日韩 国产在线 | 一区二区三区在线高清| 国产亚洲精品美女| 中国成人亚色综合网站| 亚洲午夜精品久久久久久浪潮| 一区二区三区精品国产| 午夜精品剧场| 老巨人导航500精品| 欧美久久久久久久| 国产精品日韩在线观看| 国产午夜精品全部视频在线播放 | 亚洲福利视频一区二区| 日韩视频精品在线| 亚洲欧美日韩中文在线制服| 久久国产精品一区二区三区四区| 日韩午夜中文字幕| 午夜性色一区二区三区免费视频 | 免费不卡在线视频| 亚洲免费av片| 欧美一区1区三区3区公司| 老色批av在线精品| 国产精品a级| 国产精品毛片a∨一区二区三区|国 | 亚洲综合国产精品| 久久久久欧美| 91久久精品国产91性色| 亚洲一区二区三区免费观看| 久久久精品一区| 欧美视频精品一区| 激情六月婷婷久久| 狠狠操狠狠色综合网| 国产麻豆日韩欧美久久| 亚洲精品一区二区三区福利| 欧美日韩在线亚洲一区蜜芽| 国产真实乱子伦精品视频| 一本色道久久88综合日韩精品| 久久精品成人| 日韩一区二区免费看| 久久综合国产精品| 国产农村妇女毛片精品久久麻豆| 亚洲第一精品夜夜躁人人躁| 羞羞答答国产精品www一本| 亚洲欧洲久久| 免费成人你懂的| 狠狠色狠狠色综合日日五| 午夜伦理片一区| 亚洲精品久久久久久久久久久| 中文av一区特黄| 性刺激综合网| 国产精品福利在线观看网址| 亚洲精品免费观看| 欧美国产欧美亚洲国产日韩mv天天看完整 | 国产精品一区在线播放| a91a精品视频在线观看| 老司机成人在线视频| 亚洲欧美一区二区精品久久久 | 久久久久一区二区三区| 国产亚洲亚洲| 香蕉视频成人在线观看| 一本色道久久88综合日韩精品 | 欧美三级午夜理伦三级中文幕 | 国产在线拍偷自揄拍精品| 欧美在线免费看| 亚洲欧美日韩区| 国产欧美日韩视频| 午夜精品久久久久久久白皮肤| 免费久久99精品国产自| 久久久久国产免费免费| 亚洲成人原创| 欧美成人嫩草网站| 牛牛影视久久网| 亚洲欧洲一区| 亚洲麻豆视频| 久热精品视频在线免费观看| 亚洲精品社区| 欧美亚韩一区| 久久av二区| 久久久精品久久久久| 亚洲福利视频网站| 亚洲国产成人tv| 欧美日韩亚洲一区二区三区四区| 亚洲视频精选在线| 亚洲第一页中文字幕| 亚洲综合色激情五月| 国产一区二区三区在线观看视频| 久久久综合网| 久久综合亚州| 在线亚洲电影| 欧美一二三视频| 亚洲高清视频一区二区| 最新日韩在线视频| 久热re这里精品视频在线6| 一本色道久久88精品综合| 亚洲综合首页| 亚洲国产裸拍裸体视频在线观看乱了中文 | 免费成人在线观看视频| 久久精品亚洲一区二区三区浴池| 亚洲黄色毛片| 亚洲性图久久| 亚洲国产影院| 亚洲女性裸体视频| 亚洲人成艺术| 欧美一级片久久久久久久| 国产日韩免费| 亚洲精品免费一二三区| 韩日成人av| 中国成人黄色视屏| 亚洲福利一区| 香蕉av777xxx色综合一区| 国产一区二区三区无遮挡| 亚洲另类黄色| 国产精品久久一区主播| 亚洲国产美国国产综合一区二区| 99re热这里只有精品免费视频| 狠狠入ady亚洲精品经典电影| 亚洲欧美日韩在线综合| 欧美一区二区| 国产欧美日韩精品a在线观看| 亚洲一区欧美二区| 欧美亚洲免费| 国产日韩欧美三区| 久久黄色小说| 蘑菇福利视频一区播放| 亚洲国产精品视频| 欧美精品福利| 中文av一区二区| 欧美在线视频a| 国外精品视频| 免费在线成人av| 99re66热这里只有精品4| 亚洲一区二区三区精品在线观看| 国产精品vvv| 欧美在线短视频| 亚洲国产合集| 欧美片网站免费| 亚洲影院免费| 免费在线看一区| 99精品福利视频| 国产精品视频专区| 久久亚洲一区二区| 亚洲日本aⅴ片在线观看香蕉| 亚洲在线黄色| 在线不卡欧美| 欧美日韩国产一级| 欧美一区二区精品久久911| 欧美成人亚洲成人日韩成人| 正在播放亚洲一区| 狠狠色丁香婷婷综合| 欧美日韩1区2区| 久久成人人人人精品欧| 亚洲人成人一区二区在线观看| 亚洲欧美日韩国产精品| 在线看片日韩| 国产精品女人网站| 欧美激情中文字幕乱码免费| 午夜精品福利在线| 亚洲欧洲精品一区二区三区不卡 | 中文久久乱码一区二区| 免费在线日韩av| 小嫩嫩精品导航| 在线精品视频免费观看| 国产精品毛片va一区二区三区 | 激情久久五月| 国产精品久久国产三级国电话系列| 久久久国产精品一区二区三区| 99re6热只有精品免费观看 | 亚洲三级网站| 欧美va亚洲va国产综合| 欧美一区二区三区免费观看| 亚洲精品乱码久久久久久日本蜜臀| 国产日韩欧美麻豆| 国产精品久久久久免费a∨| 欧美激情视频给我| 久热精品在线| 久久久www成人免费毛片麻豆| 在线亚洲免费视频| 99精品国产一区二区青青牛奶 | 亚洲欧洲一区二区三区在线观看 | 亚洲精品精选| 亚洲国产欧美久久|