欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go中string與[]byte高效互轉的方法實例

 更新時間:2021年09月20日 12:16:42   作者:亞洲第一中鋒_哈達迪  
string與[]byte經常需要互相轉化,普通轉化會發(fā)生底層數(shù)據的復制,下面這篇文章主要給大家介紹了關于Go中string與[]byte高效互轉的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下

前言

當我們使用go進行數(shù)據序列化或反序列化操作時,可能經常涉及到字符串和字節(jié)數(shù)組的轉換。例如:

if str, err := json.Marshal(from); err != nil {

    panic(err)

} else {

    return string(str)

}

json序列化后為[]byte類型,需要將其轉換為字符串類型。當數(shù)據量小時,類型間轉換的開銷可以忽略不計,但當數(shù)據量增大后,可能成為性能瓶頸,使用高效的轉換方法能減少這方面的開銷

數(shù)據結構

在了解其如何轉換前,需要了解其底層數(shù)據結構

本文基于go 1.13.12

string:

type stringStruct struct {

   str unsafe.Pointer

   len int

}

slice:

type slice struct {

   array unsafe.Pointer

   len   int

   cap   int

}

與slice的結構相比,string缺少一個表示容量的cap字段,因此不能對string遍歷使用內置的cap()函數(shù)那為什么string不需要cap字段呢?因為go中string被設計為不可變類型(當然在很多其他語言中也是),由于其不可像slice一樣追加元素,也就不需要cap字段判斷是否超出底層數(shù)組的容量,來決定是否擴容

只有l(wèi)en屬性不影響for-range等讀取操作,因為for-range操作只根據len決定是否跳出循環(huán)

那為什么字符串要設定為不可變呢?因為這樣能保證字符串的底層數(shù)組不發(fā)生改變

舉個例子,map中以string為鍵,如果底層字符數(shù)組改變,則計算出的哈希值也會發(fā)生變化,這樣再從map中定位時就找不到之前的value,因此其不可變特性能避免這種情況發(fā)生,string也適合作為map的鍵。除此之外,不可變特性也能保障數(shù)據的線程安全

常規(guī)實現(xiàn)

字符串不可變有很多好處,為了維持其不可變特性,字符串和字節(jié)數(shù)組互轉一般是通過數(shù)據拷貝的方式實現(xiàn):

var a string = "hello world"

var b []byte = []byte(a)  // string轉[]byte

a = string(b)             // []byte轉string

這種方式實現(xiàn)簡單,但是通過底層數(shù)據復制實現(xiàn)的,在編譯期間分別轉換成對slicebytetostring和stringtoslicebyte的函數(shù)調用

string轉[]byte

func stringtoslicebyte(buf *tmpBuf, s string) []byte {

   var b []byte

   if buf != nil && len(s) <= len(buf) {

      *buf = tmpBuf{}

      b = buf[:len(s)]

   } else {

      // 申請內存

      b = rawbyteslice(len(s))

   }

   // 復制數(shù)據

   copy(b, s)

   return b

}

其根據返回值是否逃逸到堆上,以及buf的長度是否足夠,判斷選擇使用buf還是調用rawbyteslice申請一個slice。但不管是哪種,都會執(zhí)行一次copy拷貝底層數(shù)據

[]byte轉string

func slicebytetostring(buf *tmpBuf, b []byte) (str string) {

   l := len(b)

   if l == 0 {

 return ""

   }

   if l == 1 {

      stringStructOf(&str).str = unsafe.Pointer(&staticbytes[b[0]])

      stringStructOf(&str).len = 1

      return

   }



   var p unsafe.Pointer

   if buf != nil && len(b) <= len(buf) {

      p = unsafe.Pointer(buf)

   } else {

      p = mallocgc(uintptr(len(b)), nil, false)

   }

   // 賦值底層指針

   stringStructOf(&str).str = p

   // 賦值長度

   stringStructOf(&str).len = len(b)

   // 拷貝數(shù)據

   memmove(p, (*(*slice)(unsafe.Pointer(&b))).array, uintptr(len(b)))

   return

}

首先處理長度為0或1的情況,再判斷使用buf還是通過mallocgc新申請一段內存,但無論哪種方式,最后都要拷貝數(shù)據
這里設置了轉換后字符串的len屬性

高效實現(xiàn)

如果程序保證不對底層數(shù)據進行修改,那么只轉換類型,不拷貝數(shù)據,是否可以提高性能?

unsafe.Pointer,int,uintpt這三種類型占用的內存大小相同

var v1 unsafe.Pointer

var v2 int

var v3 uintptr

fmt.Println(unsafe.Sizeof(v1)) // 8

fmt.Println(unsafe.Sizeof(v2)) // 8

fmt.Println(unsafe.Sizeof(v3)) // 8

因此從底層結構上來看string可以看做[2]uintptr,[]byte切片類型可以看做 [3]uintptr

那么從string轉[]byte只需構建出 [3]uintptr{ptr,len,len}

這里我們?yōu)閟lice結構生成了cap字段,其實這里不生成cap字段對讀取操作沒有影響,但如果要往轉換后的slice append元素可能有問題,原因如下:

這樣做slice的cap屬性是隨機的,可能是大于len的值,那么append時就不會新開辟一段內存存放元素,而是在原數(shù)組后面追加,如果后面的內存不可寫就會panic

[]byte轉string更簡單,直接轉換指針類型即可,忽略cap字段

實現(xiàn)如下:

func stringTobyteSlice(s string) []byte {

   tmp1 := (*[2]uintptr)(unsafe.Pointer(&s))

   tmp2 := [3]uintptr{tmp1[0], tmp1[1], tmp1[1]}

   return *(*[]byte)(unsafe.Pointer(&tmp2))

}



func byteSliceToString(bytes []byte) string {

   return *(*string)(unsafe.Pointer(&bytes))

}

這里使用unsafe.Pointer來轉換不同類型的指針,沒有底層數(shù)據的拷貝

性能測試

接下來對高效實現(xiàn)進行性能測試,這里選用長度為100的字符串或字節(jié)數(shù)組進行轉換

分別測試以下4個方法:

func stringTobyteSlice(s string) []byte {

   tmp1 := (*[2]uintptr)(unsafe.Pointer(&s))

   tmp2 := [3]uintptr{tmp1[0], tmp1[1], tmp1[1]}

   return *(*[]byte)(unsafe.Pointer(&tmp2))

}



func stringTobyteSliceOld(s string) []byte {

   return []byte(s)

}



func byteSliceToString(bytes []byte) string {

   return *(*string)(unsafe.Pointer(&bytes))

}



func byteSliceToStringOld(bytes []byte) string {

   return string(bytes)

}

測試結果如下:

BenchmarkStringToByteSliceOld-12            28637332                42.0 ns/op

BenchmarkStringToByteSliceNew-12            1000000000                 0.496 ns/op

BenchmarkByteSliceToStringOld-12            32595271                36.0 ns/op

BenchmarkByteSliceToStringNew-12            1000000000                 0.256 ns/op

可以看出性能差距比較大,如果需要轉換的字符串或字節(jié)數(shù)組長度更長,性能提升更加明顯

總結

本文介紹了字符串和數(shù)組的底層數(shù)據結構,以及高效的互轉方法,需要注意的是,其適用于程序能保證不對底層數(shù)據進行修改的場景。若不能保證,且底層數(shù)據被修改可能引發(fā)異常,則還是使用拷貝的方式

到此這篇關于Go中string與[]byte高效互轉的文章就介紹到這了,更多相關Go中string與[]byte互轉內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • windows下安裝make及使用makefile文件

    windows下安裝make及使用makefile文件

    這篇文章主要為大家介紹了windows下安裝make及使用makefile文件方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Go語言context上下文管理的使用

    Go語言context上下文管理的使用

    本文主要介紹了Go語言context上下文管理的使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Go語言同步與異步執(zhí)行多個任務封裝詳解(Runner和RunnerAsync)

    Go語言同步與異步執(zhí)行多個任務封裝詳解(Runner和RunnerAsync)

    這篇文章主要給大家介紹了關于Go語言同步與異步執(zhí)行多個任務封裝(Runner和RunnerAsync)的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2018-01-01
  • Golang實現(xiàn)http server提供壓縮文件下載功能

    Golang實現(xiàn)http server提供壓縮文件下載功能

    這篇文章主要介紹了Golang實現(xiàn)http server提供壓縮文件下載功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • Go?iota關鍵字與枚舉類型實現(xiàn)原理

    Go?iota關鍵字與枚舉類型實現(xiàn)原理

    這篇文章主要介紹了Go?iota關鍵字與枚舉類型實現(xiàn)原理,iota是go語言的常量計數(shù)器,只能在常量的表達式中使用,更多相關內容需要的小伙伴可以參考一下
    2022-07-07
  • golang中protobuf的使用詳解

    golang中protobuf的使用詳解

    protobuf是Google公司提出的一種輕便高效的結構化數(shù)據存儲格式,常用于結構化數(shù)據的序列化,具有語言無關、平臺無關、可擴展性特性,常用于通訊協(xié)議、服務端數(shù)據交換場景,下面我們就來看看golang中protobuf的具體使用吧
    2023-10-10
  • Go??import _ 下劃線使用

    Go??import _ 下劃線使用

    這篇文章主要為大家介紹了Go??import下劃線_使用小技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • Go語言基礎map用法及示例詳解

    Go語言基礎map用法及示例詳解

    這篇文章主要為大家介紹了Go語言基礎map的用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2021-11-11
  • GoLang中panic與recover函數(shù)以及defer語句超詳細講解

    GoLang中panic與recover函數(shù)以及defer語句超詳細講解

    這篇文章主要介紹了GoLang的panic、recover函數(shù),以及defer語句,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2023-01-01
  • Golang?手寫一個簡單的并發(fā)任務?manager

    Golang?手寫一個簡單的并發(fā)任務?manager

    這篇文章主要介紹了Golang?手寫一個簡單的并發(fā)任務?manager,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-08-08

最新評論