• <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
            <2017年3月>
            2627281234
            567891011
            12131415161718
            19202122232425
            2627282930311
            2345678


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

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            相冊

            Awesome

            Blog

            Book

            GitHub

            Link

            搜索

            •  

            積分與排名

            • 積分 - 216756
            • 排名 - 118

            最新評論

            閱讀排行榜

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

            相對于C語言,golang是類型安全的語言。但是安全的代價就是性能的妥協(xié)。 
            下面我們通過Golang中的“黑科技”來一窺Golang不想讓我們看到的“秘密”——string的底層數(shù)據(jù)。 
            通過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是一個指針,指向?qū)嶋H的數(shù)據(jù)地址,Len表示數(shù)據(jù)長度。 
            但是,在string和[]byte轉(zhuǎn)換過程中,Golang究竟悄悄幫我們做了什么,來達到安全的目的? 
            在Golang語言規(guī)范里面,string數(shù)據(jù)是禁止修改的,試圖通過&s[0], &b[0]取得string和slice數(shù)據(jù)指針地址也是不能通過編譯的。 
            下面,我們就通過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個函數(shù)的神奇之處在于,通過unsafe.Pointer和reflect.XXXHeader取到了數(shù)據(jù)首地址,并實現(xiàn)了string和[]byte的直接轉(zhuǎn)換(這些操作在語言層面是禁止的)。 
            下面我們就通過這幾個“黑科技”來測試一下語言底層的秘密:

            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

            結(jié)論如下: 
            1.string常量會在編譯期分配到只讀段,對應(yīng)數(shù)據(jù)地址不可寫入,并且相同的string常量不會重復(fù)存儲。 
            2.fmt.Sprintf生成的字符串分配在堆上,對應(yīng)數(shù)據(jù)地址可修改。 
            3.常量空字符串有數(shù)據(jù)地址,動態(tài)生成的字符串沒有設(shè)置數(shù)據(jù)地址 
            4.Golang string和[]byte轉(zhuǎn)換,會將數(shù)據(jù)復(fù)制到堆上,返回數(shù)據(jù)指向復(fù)制的數(shù)據(jù) 
            5.動態(tài)生成的字符串,即使內(nèi)容一樣,數(shù)據(jù)也是在不同的空間 
            6.只有動態(tài)生成的string,數(shù)據(jù)可以被黑科技修改 
            8.string和[]byte通過復(fù)制轉(zhuǎn)換,性能損失接近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 思月行云 閱讀(580) 評論(0)  編輯 收藏 引用 所屬分類: Golang
            久久久久亚洲av成人无码电影 | 久久99热只有频精品8| 一本一本久久A久久综合精品| 久久只有这里有精品4| 久久久久久久精品成人热色戒| 中文字幕久久精品无码| 色噜噜狠狠先锋影音久久| 久久久免费观成人影院| 日本久久久久亚洲中字幕 | 国产午夜电影久久| 伊人久久无码中文字幕| 精品久久久久久无码免费| 伊人久久久AV老熟妇色| 欧美久久久久久午夜精品| 国产午夜福利精品久久2021| 亚洲国产成人久久综合野外| 精品久久人妻av中文字幕| 精品视频久久久久| 久久91综合国产91久久精品| 无码人妻久久一区二区三区蜜桃| 国产亚洲欧美成人久久片 | 久久精品一区二区三区中文字幕| 中文精品久久久久人妻不卡| 欧美性大战久久久久久| 成人精品一区二区久久久| 久久精品aⅴ无码中文字字幕重口 久久精品a亚洲国产v高清不卡 | a级毛片无码兔费真人久久| 亚洲乱码中文字幕久久孕妇黑人 | 狠狠色丁香婷婷综合久久来| 国产激情久久久久久熟女老人 | 综合网日日天干夜夜久久| 久久综合狠狠综合久久97色| 国产精品99久久久久久宅男| 久久91亚洲人成电影网站| 国产欧美久久久精品| 国产精品美女久久久久久2018| 国产精品成人久久久| 久久这里都是精品| 中文字幕无码免费久久| 无码伊人66久久大杳蕉网站谷歌 | 久久精品国产一区二区电影|