转到:带有指针的json编码结构比使用副本慢吗?

I have the following test code:

package main

import (
    "fmt"
    "testing"
    "encoding/json"
)

type Coll1 struct {
    A string
    B string
    C string
}

type Coll2 struct {
    A *string
    B *string
    C *string
}

var as = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
var bs = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
var cs = "ccccccccccccccccccccccccccccccccc"

func testBM1(b *testing.B) {
    for i := 0; i<b.N; i++ {
        json.Marshal(Coll1{as,bs,cs})
    }
}

func testBM2(b *testing.B) {
    for i := 0; i<b.N; i++ {
        json.Marshal(Coll2{&as,&bs,&cs})
    }
}

func main() {
    fmt.Println(testing.Benchmark(testBM1))
    fmt.Println(testing.Benchmark(testBM2))
}

I would expect the second case to run faster since it is using pointers and therefore doesn't have to copy the strings, but in fact it runs at about 4250 ns/op where the first runs near 2800 ns/op. Can anyone shed any light on why this might be?

Edit: Darshan Computing suggested that this may hold true for embedded structs even. A simple test confirms this:

package main

import (
    "fmt"
    "testing"                  
    "encoding/json"            
) 

type Coll1 struct {
    A,B,C string
}

type Coll1Outer struct {
    A,B,C Coll1
}

type Coll2Outer struct {
    A,B,C *Coll2
}

type Coll2 struct {
    A,B,C *string
}

var as = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
var bs = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
var cs = "ccccccccccccccccccccccccccccccccc"

func testBM1(b *testing.B) {
    for i := 0; i<b.N; i++ {
        c := Coll1Outer{ Coll1{as,bs,cs}, 
                         Coll1{as,bs,cs},                
                         Coll1{as,bs,cs} }               
        json.Marshal(c)
    }
}

func testBM2(b *testing.B) {
    for i := 0; i<b.N; i++ {
        c := Coll2Outer{ &Coll2{&as,&bs,&cs},
                         &Coll2{&as,&bs,&cs},            
                         &Coll2{&as,&bs,&cs} }           
        json.Marshal(c)
    }
}

func main() {
    fmt.Println(testing.Benchmark(testBM1))
    fmt.Println(testing.Benchmark(testBM2))
}

For me this shows the non-pointer struct taking about 12ms/op, while the one with pointers takes 13ms/op. Not a huge difference, but it's interesting that the property still holds.

I notice the biggest percentage difference in ns/op when I set as, bs, and cs to "a", "b", and "c", respectively. As I increase the length of the strings, they approach each other. They seem to always be about 1000 ns/op different.

So I believe all that's going on is that it takes 1000 ns on my machine (1450 on yours) to reflect and follow the pointers. Passing a smaller struct up front doesn't seem to counteract this effect because once the pointers are followed, Marshal still passes the data around internally in the process of generating and returning the equivalent JSON.