2013-05-31 2 views
2

je le code de test suivant:Go: json codage struct avec des pointeurs est plus lent qu'avec des copies?

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)) 
} 

je me attends le second cas, de courir plus vite, car il utilise des pointeurs et ne dispose donc pas de copier les chaînes, mais en fait, il fonctionne à environ 4250 ns/op où la première tourne près de 2800 ns/op. Quelqu'un peut-il faire la lumière sur pourquoi cela pourrait être? Edit: Darshan Computing a suggéré que cela pourrait même être vrai pour les structures intégrées. Un simple test confirme:

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)) 
} 

Pour moi, cela montre la struct non-pointeur prenant environ 12ms/op, tandis que celui des pointeurs prend 13ms/op. Pas une énorme différence, mais c'est intéressant que la propriété tient toujours.

+1

J'ai réécrit votre code pour profiter des fonctionnalités de benchmarking intégrées (voir http://play.golang.org/p/90fYFCSjN9) et j'ai obtenu 3729 ns/op pour le premier et 4600 ns/op pour le second sur win8_64, semble toujours faux, mais les valeurs sont plus proches. – Intermernet

+1

Et, si je remplace les appels 'json.Marshal' par des appels' _ = fmt.Sprintf', j'obtiens respectivement 11990 ns/op et 7098 ns/op (rendant les pointeurs presque 70% plus rapides). Il se passe quelque chose dans la fonction json.Marshall qui ralentit l'utilisation des pointeurs. – Intermernet

+1

Après un peu plus de recherche, je pense que c'est parce que la fonction 'marshal' dans' encoding/json' appelle 'reflect.ValueOf()' sur l'interface d'entrée (http://golang.org/src/pkg/encoding/json/ encode.go? s = 7722: 7964 # L230) qui peut être plus lente si elle doit trouver le type d'une valeur représentée par un pointeur, plutôt que directement sur la valeur itslef. Juste une supposition cependant. – Intermernet

Répondre

1

je remarque la plus grande différence de pourcentage ns/op quand je mis as, bs et cs-"a", "b" et "c", respectivement. Au fur et à mesure que j'augmente la longueur des cordes, elles se rapprochent l'une de l'autre. Ils semblent toujours être environ 1000 ns/op différents.

Donc, je crois que tout ce qui se passe est que cela prend 1000 ns sur ma machine (1450 sur le vôtre) pour refléter et suivre les pointeurs. Le passage d'une structure plus petite à l'avant ne semble pas contrecarrer cet effet car une fois que les pointeurs sont suivis, Marshal passe toujours les données en interne lors du processus de génération et de retour du JSON équivalent.

+0

de darshan Cela a du sens, je n'avais pas pensé au fait qu'il va avoir besoin d'une sorte de réflexion. Pour un plus grand système intégré de structs (plusieurs couches profondes, de nombreux champs) aurais-je raison de supposer que les pointeurs seraient généralement plus rapides dans ce cas? –

+0

@MediocreGopher Ma conjecture est que le même modèle contiendrait même pour les structures complexes, mais je serais certainement intéressé par les résultats si vous le tester. –

+0

J'ai édité ma question originale avec un cas de test de base pour les structures intégrées, on dirait que vous avez raison. Je suis toujours curieux de savoir pourquoi la réflexion sur les pointeurs prend tellement de temps à faire. Je suppose que la réflexion doit être faite même sur les types non-pointeurs, est-ce juste que les non-pointeurs ne nécessitent pas forcément un "saut" supplémentaire pour accéder aux données réelles? –

Questions connexes