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

Fork me on GitHub
隨筆 - 215  文章 - 13  trackbacks - 0
<2025年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011


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

常用鏈接

留言簿(1)

隨筆分類

隨筆檔案

相冊

Awesome

Blog

Book

GitHub

Link

搜索

  •  

積分與排名

  • 積分 - 219784
  • 排名 - 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 思月行云 閱讀(177) 評論(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>
            国产亚洲成精品久久| 久久精品一区二区三区四区| 裸体一区二区三区| 99精品视频免费观看视频| 最新日韩在线视频| 国产欧美日韩免费看aⅴ视频| 久久精品电影| 欧美精品在线观看播放| 欧美一区二区三区男人的天堂 | 日韩视频不卡| 亚洲欧美激情视频| 久久精品夜夜夜夜久久| 在线免费观看欧美| 国产亚洲aⅴaaaaaa毛片| 亚洲激情av| 一区在线播放| 欧美专区福利在线| 午夜一级久久| 国产精品不卡在线| 亚洲无人区一区| 亚洲一区二区毛片| 欧美色欧美亚洲另类七区| 亚洲美女尤物影院| 亚洲精品极品| 欧美日韩午夜精品| 一本一道久久综合狠狠老精东影业| 99av国产精品欲麻豆| 欧美日韩精品一区二区在线播放| 欧美激情在线播放| 一区二区高清视频| 欧美图区在线视频| 羞羞答答国产精品www一本| 午夜视频精品| 一区二区三区在线视频观看| 久久久久网址| 日韩一区二区精品视频| 亚洲一区二区三区视频播放| 国产伦精品一区二区三区视频黑人| 午夜精品美女久久久久av福利| 老司机精品久久| 在线亚洲自拍| 亚洲高清久久久| 国产一区二区久久久| 欧美成人免费网站| 亚洲欧洲av一区二区三区久久| 欧美激情第二页| 美女尤物久久精品| 久久er精品视频| 亚洲午夜激情网站| 你懂的国产精品| 性8sex亚洲区入口| 正在播放日韩| 亚洲三级电影在线观看 | 午夜亚洲激情| 国产精品99久久久久久宅男| 亚洲国产日本| 亚洲国产精品精华液网站| 欧美专区在线播放| 午夜精品久久久久久久99樱桃| 亚洲老板91色精品久久| 亚洲全黄一级网站| 91久久久亚洲精品| 亚洲欧洲日本国产| 日韩写真视频在线观看| 一区二区电影免费观看| 日韩视频永久免费| avtt综合网| 亚洲免费视频一区二区| 午夜精品美女自拍福到在线| 亚洲欧美综合另类中字| 久久精品国产99国产精品| 久久久久久久久伊人| 久久影院午夜论| 欧美日韩高清在线| 国产视频精品va久久久久久| 亚洲国产欧美日韩另类综合| 亚洲美女在线视频| 欧美一级久久久| 免费欧美在线视频| 亚洲精品久久久久久久久| 亚洲精品孕妇| 久久久久久**毛片大全| 欧美视频一二三区| 国产综合精品一区| 一区二区av在线| 久久久爽爽爽美女图片| 日韩亚洲一区二区| 欧美在线三级| 国产精品自拍网站| 日韩视频一区二区在线观看 | 亚洲国产精品成人| 久久精品成人欧美大片古装| 欧美激情亚洲自拍| 亚洲黄页一区| 欧美成人69av| 亚洲欧美一区二区精品久久久| 欧美三级日韩三级国产三级| 亚洲黑丝在线| 欧美激情视频在线播放| 久久久91精品国产一区二区精品| 国产精品久久久久久久久动漫 | 国产精品日韩欧美大师| 亚洲一区在线免费观看| 亚洲欧美日本日韩| 国产精品激情电影| 亚洲欧美三级在线| 午夜精品理论片| 一区二区三区在线视频观看| 欧美成人精品福利| 麻豆成人91精品二区三区| 亚洲电影免费观看高清完整版在线观看 | 欧美二区在线观看| 欧美成人午夜| 亚洲午夜激情网页| 亚洲欧美在线看| 亚洲国产成人久久| 亚洲视频综合在线| 亚洲精品久久久久久一区二区| 一区二区三区www| 国产在线观看精品一区二区三区| 女女同性精品视频| 欧美日韩一区二区三区四区五区 | 欧美在线视频导航| 亚洲国产成人在线视频| 日韩视频在线观看一区二区| 激情欧美一区二区三区在线观看| 亚洲精品三级| 黄色成人av网站| 亚洲无限av看| 一区二区三区高清| 六月丁香综合| 久久一本综合频道| 国产女优一区| 亚洲欧美日韩成人| 亚洲欧美日韩一区二区三区在线| 欧美成人自拍| 亚洲国产导航| 9久re热视频在线精品| 美国成人毛片| 日韩视频一区二区| 亚洲夜晚福利在线观看| 欧美日本三级| 亚洲一区二区三区色| 欧美亚洲视频一区二区| 国产视频亚洲精品| 久久久国产91| 91久久精品日日躁夜夜躁欧美 | 蜜桃伊人久久| 曰韩精品一区二区| 蜜臀av一级做a爰片久久| 亚洲国产欧美不卡在线观看| 亚洲免费观看在线观看| 欧美日一区二区在线观看| 亚洲在线网站| 亚洲大胆在线| 午夜精品久久久久| 极品裸体白嫩激情啪啪国产精品| 欧美激情第10页| 亚洲永久网站| 亚洲第一在线综合网站| 亚洲欧美精品中文字幕在线| 国内免费精品永久在线视频| 欧美精品激情在线观看| 欧美影院成人| 日韩视频久久| 欧美国产国产综合| 久久一区国产| 久久久久久国产精品mv| 亚洲免费视频中文字幕| 在线看国产日韩| 国产日韩欧美一区二区| 欧美日韩一区自拍| 美国十次成人| 久久久久www| 欧美一区高清| 欧美中文字幕第一页| 国产精品99久久不卡二区| 亚洲国产日韩综合一区| 亚洲国产精品成人综合色在线婷婷 | 久久久www成人免费无遮挡大片| 这里只有精品视频| 亚洲一区二区在线| 亚洲欧美日韩精品一区二区| 一本久久综合| 亚洲一区二区三区免费观看 | 国内自拍一区| 激情亚洲网站| 亚洲美女性视频| 香蕉久久精品日日躁夜夜躁| 久久精品国产亚洲一区二区三区| 久久精品国产一区二区三区免费看| 欧美影院成人| 欧美激情二区三区| 一区二区三区日韩欧美| 午夜精品久久久久久久久久久久久| 久久精品一级爱片| 欧美日本在线看| 一区在线视频| 久久精品一区二区国产| 亚洲国产精品激情在线观看|