• <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年3月>
            282912345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789


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

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            相冊

            Awesome

            Blog

            Book

            GitHub

            Link

            搜索

            •  

            積分與排名

            • 積分 - 215473
            • 排名 - 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常量會在編譯期分配到只讀段,對應數(shù)據(jù)地址不可寫入,并且相同的string常量不會重復存儲。 
            2.fmt.Sprintf生成的字符串分配在堆上,對應數(shù)據(jù)地址可修改。 
            3.常量空字符串有數(shù)據(jù)地址,動態(tài)生成的字符串沒有設置數(shù)據(jù)地址 
            4.Golang string和[]byte轉(zhuǎn)換,會將數(shù)據(jù)復制到堆上,返回數(shù)據(jù)指向復制的數(shù)據(jù) 
            5.動態(tài)生成的字符串,即使內(nèi)容一樣,數(shù)據(jù)也是在不同的空間 
            6.只有動態(tài)生成的string,數(shù)據(jù)可以被黑科技修改 
            8.string和[]byte通過復制轉(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 思月行云 閱讀(578) 評論(0)  編輯 收藏 引用 所屬分類: Golang
            国产精品免费久久久久影院| 久久久久99精品成人片| 久久笫一福利免费导航| 女同久久| 久久精品夜夜夜夜夜久久| 久久国产精品久久精品国产| 久久夜色撩人精品国产| 久久国产免费观看精品3| 国产亚州精品女人久久久久久 | 亚洲综合久久夜AV | 中文国产成人精品久久不卡| 久久无码av三级| 77777亚洲午夜久久多人| 亚洲国产精品热久久| 99久久国产宗和精品1上映| 久久综合狠狠综合久久激情 | 一本色道久久88综合日韩精品| 99精品久久精品一区二区| 亚洲а∨天堂久久精品9966| 夜夜亚洲天天久久| 久久这里都是精品| 精品久久久久久99人妻| 国产一级持黄大片99久久| 三级三级久久三级久久 | 久久精品国产一区二区三区不卡 | 亚洲Av无码国产情品久久| 久久精品国产半推半就| 亚洲αv久久久噜噜噜噜噜| 久久亚洲AV无码西西人体| 国产69精品久久久久99| 国产国产成人精品久久| jizzjizz国产精品久久| 亚洲AV成人无码久久精品老人| 99久久香蕉国产线看观香| 要久久爱在线免费观看| 久久夜色精品国产噜噜亚洲a| 久久中文字幕视频、最近更新| 久久嫩草影院免费看夜色| 久久免费视频一区| 久久久精品国产| 亚洲精品乱码久久久久久自慰|