• <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>
            Fork me on GitHub
            隨筆 - 215  文章 - 13  trackbacks - 0
            <2016年9月>
            28293031123
            45678910
            11121314151617
            18192021222324
            2526272829301
            2345678


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

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            相冊

            Awesome

            Blog

            Book

            GitHub

            Link

            搜索

            •  

            積分與排名

            • 積分 - 215436
            • 排名 - 118

            最新評論

            閱讀排行榜

            http://blog.csdn.net/vipally/article/details/52940119

            相對于C語言,golang是類型安全的語言。但是安全的代價就是性能的妥協。 
            下面我們通過Golang中的“黑科技”來一窺Golang不想讓我們看到的“秘密”——string的底層數據。 
            通過reflect包,我們可以知道,在Golang底層,string和slice其實都是struct:

            type SliceHeader struct {
                Data uintptr
                Len  int
                Cap  int
            }
            type StringHeader struct {
                Data uintptr
                Len  int
            }
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9

            其中Data是一個指針,指向實際的數據地址,Len表示數據長度。 
            但是,在string和[]byte轉換過程中,Golang究竟悄悄幫我們做了什么,來達到安全的目的? 
            在Golang語言規范里面,string數據是禁止修改的,試圖通過&s[0], &b[0]取得string和slice數據指針地址也是不能通過編譯的。 
            下面,我們就通過Golang的“黑科技”來一窺Golang背后的“秘密”。

            //return GoString's buffer slice(enable modify string)
            func StringBytes(s string) Bytes {
                return *(*Bytes)(unsafe.Pointer(&s))
            }
            
            // convert b to string without copy
            func BytesString(b []byte) String {
                return *(*String)(unsafe.Pointer(&b))
            }
            
            // 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 BytesPointer(b []byte) unsafe.Pointer {
                p := (*reflect.SliceHeader)(unsafe.Pointer(&b))
                return unsafe.Pointer(p.Data)
            }
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
            • 19
            • 20
            • 21
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
            • 19
            • 20
            • 21

            以上4個函數的神奇之處在于,通過unsafe.Pointer和reflect.XXXHeader取到了數據首地址,并實現了string和[]byte的直接轉換(這些操作在語言層面是禁止的)。 
            下面我們就通過這幾個“黑科技”來測試一下語言底層的秘密:

            func TestPointer(t *testing.T) {
                s := []string{
                    "",
                    "",
                    "hello",
                    "hello",
                    fmt.Sprintf(""),
                    fmt.Sprintf(""),
                    fmt.Sprintf("hello"),
                    fmt.Sprintf("hello"),
                }
                fmt.Println("String to bytes:")
                for i, v := range s {
                    b := unsafe.StringBytes(v)
                    b2 := []byte(v)
                    if b.Writeable() {
                        b[0] = 'x'
                    }
                    fmt.Printf("%d\ts=%5s\tptr(v)=%-12v\tptr(StringBytes(v)=%-12v\tptr([]byte(v)=%-12v\n",
                        i, v, unsafe.StringPointer(v), b.Pointer(), unsafe.BytesPointer(b2))
                }
            
                b := [][]byte{
                    []byte{},
                    []byte{'h', 'e', 'l', 'l', 'o'},
                }
                fmt.Println("Bytes to string:")
                for i, v := range b {
                    s1 := unsafe.BytesString(v)
                    s2 := string(v)
                    fmt.Printf("%d\ts=%5s\tptr(v)=%-12v\tptr(StringBytes(v)=%-12v\tptr(string(v)=%-12v\n",
                        i, s1, unsafe.BytesPointer(v), s1.Pointer(), unsafe.StringPointer(s2))
                }
            
            }
            
            const N = 3000000
            
            func Benchmark_Normal(b *testing.B) {
                for i := 1; i < N; i++ {
                    s := fmt.Sprintf("12345678901234567890123456789012345678901234567890")
                    bb := []byte(s)
                    bb[0] = 'x'
                    s = string(bb)
                    s = s
                }
            }
            func Benchmark_Direct(b *testing.B) {
                for i := 1; i < N; i++ {
                    s := fmt.Sprintf("12345678901234567890123456789012345678901234567890")
                    bb := unsafe.StringBytes(s)
                    bb[0] = 'x'
                    s = s
                }
            }
            
            //test result
            //String to bytes:
            //0 s=      ptr(v)=0x51bd70     ptr(StringBytes(v)=0x51bd70     ptr([]byte(v)=0xc042021c58
            //1 s=      ptr(v)=0x51bd70     ptr(StringBytes(v)=0x51bd70     ptr([]byte(v)=0xc042021c58
            //2 s=hello ptr(v)=0x51c2fa     ptr(StringBytes(v)=0x51c2fa     ptr([]byte(v)=0xc042021c58
            //3 s=hello ptr(v)=0x51c2fa     ptr(StringBytes(v)=0x51c2fa     ptr([]byte(v)=0xc042021c58
            //4 s=      ptr(v)=<nil>        ptr(StringBytes(v)=<nil>        ptr([]byte(v)=0xc042021c58
            //5 s=      ptr(v)=<nil>        ptr(StringBytes(v)=<nil>        ptr([]byte(v)=0xc042021c58
            //6 s=xello ptr(v)=0xc0420444b5 ptr(StringBytes(v)=0xc0420444b5 ptr([]byte(v)=0xc042021c58
            //7 s=xello ptr(v)=0xc0420444ba ptr(StringBytes(v)=0xc0420444ba ptr([]byte(v)=0xc042021c58
            //Bytes to string:
            //0 s=      ptr(v)=0x5c38b8     ptr(StringBytes(v)=0x5c38b8     ptr(string(v)=<nil>
            //1 s=hello ptr(v)=0xc0420445e0 ptr(StringBytes(v)=0xc0420445e0 ptr(string(v)=0xc042021c38
            //Benchmark_Normal-4    1000000000           0.87 ns/op
            //Benchmark_Direct-4    2000000000           0.24 ns/op
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
            • 19
            • 20
            • 21
            • 22
            • 23
            • 24
            • 25
            • 26
            • 27
            • 28
            • 29
            • 30
            • 31
            • 32
            • 33
            • 34
            • 35
            • 36
            • 37
            • 38
            • 39
            • 40
            • 41
            • 42
            • 43
            • 44
            • 45
            • 46
            • 47
            • 48
            • 49
            • 50
            • 51
            • 52
            • 53
            • 54
            • 55
            • 56
            • 57
            • 58
            • 59
            • 60
            • 61
            • 62
            • 63
            • 64
            • 65
            • 66
            • 67
            • 68
            • 69
            • 70
            • 71
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
            • 19
            • 20
            • 21
            • 22
            • 23
            • 24
            • 25
            • 26
            • 27
            • 28
            • 29
            • 30
            • 31
            • 32
            • 33
            • 34
            • 35
            • 36
            • 37
            • 38
            • 39
            • 40
            • 41
            • 42
            • 43
            • 44
            • 45
            • 46
            • 47
            • 48
            • 49
            • 50
            • 51
            • 52
            • 53
            • 54
            • 55
            • 56
            • 57
            • 58
            • 59
            • 60
            • 61
            • 62
            • 63
            • 64
            • 65
            • 66
            • 67
            • 68
            • 69
            • 70
            • 71

            結論如下: 
            1.string常量會在編譯期分配到只讀段,對應數據地址不可寫入,并且相同的string常量不會重復存儲。 
            2.fmt.Sprintf生成的字符串分配在堆上,對應數據地址可修改。 
            3.常量空字符串有數據地址,動態生成的字符串沒有設置數據地址 
            4.Golang string和[]byte轉換,會將數據復制到堆上,返回數據指向復制的數據 
            5.動態生成的字符串,即使內容一樣,數據也是在不同的空間 
            6.只有動態生成的string,數據可以被黑科技修改 
            8.string和[]byte通過復制轉換,性能損失接近4倍

            我將測試代碼放在這里,歡迎參考: 
            https://github.com/vipally/gx/blob/master/unsafe/string_test.go

            參考資料: 
            [1] Go語言黑魔法 http://studygolang.com/articles/2909

            posted on 2017-05-04 10:32 思月行云 閱讀(578) 評論(0)  編輯 收藏 引用 所屬分類: Golang
            国内精品久久国产| 久久国产精品无码一区二区三区 | 久久精品夜夜夜夜夜久久| 精品无码久久久久久久动漫| 国产午夜福利精品久久2021| 麻豆成人久久精品二区三区免费| 波多野结衣久久一区二区| 伊人久久精品影院| 一本久久a久久精品综合香蕉| 久久久久久久久久久免费精品| 久久97久久97精品免视看| 国产精品成人99久久久久| 国产叼嘿久久精品久久| 久久久99精品一区二区 | 久久综合九色综合久99| 久久久久亚洲AV综合波多野结衣| 久久精品无码av| 中文字幕无码久久人妻| 欧美精品国产综合久久| 欧美一区二区三区久久综 | 国产亚洲精久久久久久无码AV| 国产高潮国产高潮久久久91| 国产亚洲精午夜久久久久久| 久久国产精品国语对白| 无码国内精品久久人妻麻豆按摩 | 久久精品国产男包| 色欲综合久久中文字幕网| 久久久久亚洲AV无码麻豆| 久久综合欧美成人| 久久精品这里只有精99品| 狠狠色婷婷久久综合频道日韩| AV无码久久久久不卡蜜桃| www.久久热| 一级做a爰片久久毛片看看| 综合人妻久久一区二区精品| 国产精品久久久天天影视| 久久伊人五月天论坛| 久久99国产精品尤物| 久久精品这里只有精99品| 久久香蕉超碰97国产精品| 99久久人人爽亚洲精品美女|