Golang利用位運(yùn)算實(shí)現(xiàn)為程序加速
前言
最近在持續(xù)優(yōu)化之前編寫(xiě)的 JSON
解析庫(kù) xjson,主要是兩個(gè)方面的優(yōu)化。
第一個(gè)是支持將一個(gè) JSONObject
對(duì)象輸出為 JSON
字符串。
這點(diǎn)在上個(gè)版本中只是利用自帶的 Print
函數(shù)打印數(shù)據(jù):
func TestJson4(t *testing.T) { str := `{"people":{"name":{"first":"bob"}}}` first := xjson.Get(str, "people.name.first") assert.Equal(t, first.String(), "bob") get := xjson.Get(str, "people") fmt.Println(get.String()) //assert.Equal(t, get.String(),`{"name":{"first":"bob"}}`) }
Output:
map[name:map[first:bob]]
本次優(yōu)化之后便能直接輸出 JSON 字符串了:
實(shí)現(xiàn)過(guò)程也很簡(jiǎn)單,只需要遞歸遍歷 object 中的數(shù)據(jù),然后拼接字符串即可,核心代碼如下:
func (r Result) String() string { switch r.Token { case String: return fmt.Sprint(r.object) case Bool: return fmt.Sprint(r.object) case Number: i, _ := strconv.Atoi(fmt.Sprint(r.object)) return fmt.Sprintf("%d", i) case Float: i, _ := strconv.ParseFloat(fmt.Sprint(r.object), 64) return fmt.Sprintf("%f", i) case JSONObject: return object2JSONString(r.object) case ArrayObject: return object2JSONString(r.Array()) default: return "" } }
用位運(yùn)算優(yōu)化
第二個(gè)優(yōu)化主要是提高了性能,查詢一個(gè)復(fù)雜 JSON 數(shù)據(jù)的時(shí)候性能提高了大約 ?16%.
# 優(yōu)化前 BenchmarkDecode-12 90013 66905 ns/op 42512 B/op 1446 allocs/op # 優(yōu)化后 BenchmarkDecode-12 104746 59766 ns/op 37749 B/op 1141 allocs/op
這里截取了一些重點(diǎn)改動(dòng)的部分:
在 JSON 解析過(guò)程中會(huì)有一個(gè)有限狀態(tài)機(jī)狀態(tài)遷移的過(guò)程,而遷移的時(shí)候可能會(huì)出現(xiàn)多個(gè)狀態(tài)。
比如當(dāng)前解析到的 token 值為 {
,那它接下來(lái)的 token 可能會(huì)為 ObjectKey:"name"
,也可能會(huì)是 BeginObject:{
,當(dāng)然也可能會(huì)是 EndObject:}
,
所以在優(yōu)化之前我是將狀態(tài)全部存放在一個(gè)集合中的,在解析過(guò)程中如果發(fā)現(xiàn)狀態(tài)不滿足預(yù)期的列表時(shí)則會(huì)拋出語(yǔ)法異常的錯(cuò)誤。
所以優(yōu)化之前是遍歷這個(gè)集合來(lái)進(jìn)行判斷的,這樣的時(shí)間復(fù)雜度為 O(N)
,但當(dāng)我們換成位運(yùn)算就不一樣了,時(shí)間復(fù)雜度直接就變?yōu)?code>O(1)了,同時(shí)還節(jié)省了一個(gè)切片的存儲(chǔ)空間。
我們簡(jiǎn)單來(lái)分析下這個(gè)位運(yùn)算為什么會(huì)達(dá)到判斷一個(gè)數(shù)據(jù)是否在一個(gè)集合中同樣的效果。
首先以這兩個(gè)狀態(tài)為例:
StatusObjectKey status = 0x0002 StatusColon status = 0x0004
他們分別對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)為:
StatusObjectKey status = 0x0002 //0010 StatusColon status = 0x0004 //0100
當(dāng)我們對(duì)這兩個(gè)數(shù)據(jù)求 |
運(yùn)算得到的數(shù)據(jù)是 0110
:
A:0010 B:0100 C:0110
這時(shí)候如何我們?nèi)绻眠@兩個(gè)原始數(shù)據(jù)與 C:0110
做 &
運(yùn)算時(shí)就會(huì)還原為剛才的兩個(gè)數(shù)據(jù)。
// input: A:0010 C:0110 // output: A:0010 ---------- // input: B:0100 C:0110 // output: B:0100
但我們換一個(gè) D 與 C 求 &
時(shí):
D: 1000 // 0x0008 對(duì)應(yīng)的二進(jìn)制為 1000 C: 0110 D':0000
將會(huì)得到一個(gè) 0 值,只要得出的數(shù)據(jù)大于 0 我們就能判斷一個(gè)數(shù)據(jù)是否在給定的集合中了。
當(dāng)然這里有一個(gè)前提條件就是,我們輸入的數(shù)據(jù)高位永遠(yuǎn)都是是 1 才行,也就是2的冪。
同樣的優(yōu)化在解析查詢語(yǔ)法時(shí)也有使用:
其他奇淫巧技
當(dāng)然位運(yùn)算還有一些其他技巧,比如判斷奇偶數(shù):
// 偶數(shù) a & 1 == 0 // 奇數(shù) a & 1 == 1
乘法和除法,右移1一位是除以2,左移一位是乘以2.
x := 2 fmt.Println(x>>1) //1 fmt.Println(x<<1) //4
總結(jié)
位運(yùn)算在帶來(lái)程序性能提升的同時(shí)也降低代碼可讀性,所以我們得按需選擇是否使用;
再一些底層庫(kù)、框架代碼對(duì)性能有極致追求的場(chǎng)景推薦使用,但在業(yè)務(wù)代碼中對(duì)數(shù)據(jù)做加減乘除就沒(méi)必要用位運(yùn)算了,只會(huì)讓后續(xù)的維護(hù)者一臉懵逼。
到此這篇關(guān)于Golang利用位運(yùn)算實(shí)現(xiàn)為程序加速的文章就介紹到這了,更多相關(guān)Golang位運(yùn)算內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用golang實(shí)現(xiàn)pdf中自動(dòng)換行的表格
這篇文章主要給大家介紹了如何利用golang實(shí)現(xiàn)pdf中自動(dòng)換行的表格,文中通過(guò)代碼示例給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-02-02Hugo 游樂(lè)場(chǎng)內(nèi)容初始化示例詳解
這篇文章主要為大家介紹了Hugo 游樂(lè)場(chǎng)內(nèi)容初始化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Go實(shí)現(xiàn)mongodb增刪改查工具類的代碼示例
這篇文章主要給大家介紹了關(guān)于Go實(shí)現(xiàn)mongodb增刪改查工具類的相關(guān)資料,MongoDB是一個(gè)NoSQL數(shù)據(jù)庫(kù),它提供了靈活的文檔存儲(chǔ)模型以及強(qiáng)大的查詢和操作功能,需要的朋友可以參考下2023-10-10Go語(yǔ)言學(xué)習(xí)之new函數(shù)的用法詳解
這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中new()函數(shù)的相關(guān)知識(shí)以及具體用法,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-05-05Go語(yǔ)言面向?qū)ο笾械亩鄳B(tài)你學(xué)會(huì)了嗎
面向?qū)ο笾械亩鄳B(tài)(Polymorphism)是指一個(gè)對(duì)象可以具有多種不同的形態(tài)或表現(xiàn)方式,本文將通過(guò)一些簡(jiǎn)單的示例為大家講解一下多態(tài)的實(shí)現(xiàn),需要的可以參考下2023-07-07