Last active
January 30, 2022 13:01
-
-
Save filevich/0f26cd5ec986e0b61830231cafc523e2 to your computer and use it in GitHub Desktop.
golang json slice benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"encoding/json" | |
"fmt" | |
"testing" | |
) | |
/* | |
conclusion: | |
1. Dado un mapa, como hacer un json de sus valores unicamente (json(dic.values())) | |
La forma mas rapida es hacer un slice de punteros a las referencias de los | |
elementos. | |
ganador: BenchmarkMarshalMaskPointers2-4 | |
2. Dada una lista de elementos, como hacer que, segun su contenido cambie el | |
formato del json resultante. | |
(uso: en la struct "Entry": si tiene 1 solo jugador -> que no retorne una lista | |
sino solo un objeto jugador). | |
ganador: BenchmarkMarshaler1 | |
3. Como cambiar la vision de una struct: | |
e.g., Una struct tiene campos privados y quiero crear un JSON para usuarios | |
pero que omita esos campos. | |
BenchmarkMask1 (respaldar,nulear, restaurar) y BenchmarkMask2 (copiar a una struct | |
independiente solo con los campos que queremos) muestran que ambos metodos son | |
practicamente igual de performantes. | |
*/ | |
const n = 10000000 | |
// const n = 2 | |
type Baz struct { | |
A int `json:"a"` | |
B bool `json:"b"` | |
} | |
/* | |
// prubea sin deltas: | |
BenchmarkMarshalAll-4 1000000000 0.2059 ns/op | |
BenchmarkMarshalEach-4 1000000000 0.7079 ns/op | |
BenchmarkMarshalMaskPointers-4 1000000000 0.3866 ns/op | |
BenchmarkMarshalMaskPointers2-4 1000000000 0.2189 ns/op | |
BenchmarkMarshalMaskValues-4 1000000000 0.4475 ns/op | |
// entonces performance segun los ns/op | |
1.0 | |
3.43 | |
1.87 | |
1.06 | |
2.17 | |
*/ | |
// from fib_test.go | |
func BenchmarkMarshalAll(b *testing.B) { | |
// ti := time.Now() | |
var xs [n]Baz | |
for i := 0; i < n; i++ { | |
xs[i] = Baz{A: 22, B: true} | |
} | |
_, err := json.Marshal(xs) | |
if err != nil { | |
b.Error(err) | |
} | |
// b.Log(string(bs)) | |
// tf := time.Now() | |
// b.Logf("Delta: %f\n", tf.Sub(ti).Seconds()) | |
} | |
func BenchmarkMarshalEach(b *testing.B) { | |
// ti := time.Now() | |
var xs [n]Baz | |
for i := 0; i < n; i++ { | |
xs[i] = Baz{A: 22, B: true} | |
} | |
open := []byte(`[`) | |
close := []byte(`]`) | |
cont := make([][]byte, 0) | |
for i := 0; i < n; i++ { | |
bs, _ := json.Marshal(xs[i]) | |
cont = append(cont, bs) | |
} | |
if nil == append(open, | |
append( | |
bytes.Join(cont, []byte(`,`)), | |
close..., | |
)..., | |
) { | |
b.Error("err!") | |
} | |
// b.Log(string(res2)) | |
// tf := time.Now() | |
// b.Logf("Delta: %f\n", tf.Sub(ti).Seconds()) | |
} | |
func BenchmarkMarshalMaskPointers1(b *testing.B) { | |
// ti := time.Now() | |
var xs [n]Baz | |
for i := 0; i < n; i++ { | |
xs[i] = Baz{A: 22, B: true} | |
} | |
values := make([]*Baz, n) | |
for ix := range xs { | |
values = append(values, &xs[ix]) | |
} | |
_, err := json.Marshal(values) | |
if err != nil { | |
b.Error(err) | |
} | |
// tf := time.Now() | |
// b.Logf("Delta: %f\n", tf.Sub(ti).Seconds()) | |
} | |
func BenchmarkMarshalMaskPointers2(b *testing.B) { | |
// ti := time.Now() | |
var xs [n]Baz | |
for i := 0; i < n; i++ { | |
xs[i] = Baz{A: 22, B: true} | |
} | |
values := make([]*Baz, n) | |
for ix := range xs { | |
values[ix] = &xs[ix] | |
} | |
_, err := json.Marshal(values) | |
if err != nil { | |
b.Error(err) | |
} | |
// tf := time.Now() | |
// b.Logf("Delta: %f\n", tf.Sub(ti).Seconds()) | |
} | |
func BenchmarkMarshalMaskValues(b *testing.B) { | |
// ti := time.Now() | |
var xs [n]Baz | |
for i := 0; i < n; i++ { | |
xs[i] = Baz{A: 22, B: true} | |
} | |
values := make([]Baz, n) | |
for ix := range xs { | |
values = append(values, xs[ix]) | |
} | |
_, err := json.Marshal(values) | |
if err != nil { | |
b.Error(err) | |
} | |
// tf := time.Now() | |
// b.Logf("Delta: %f\n", tf.Sub(ti).Seconds()) | |
} | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// si haces json.Mashal([n]Foo) (ie. array) --> no va a llamar al marshaller custom | |
// si haces json.Mashal([]Foo) (ie. slice) --> SI va a llamar al marshaller custom | |
// hs := [2]Xoo{ | |
// {Height: 134, Weight: 666}, | |
// {Height: 70, Weight: 50}, | |
// } | |
// hs := []Xoo{ | |
// {Height: 134, Weight: 666}, | |
// {Height: 70, Weight: 50}, | |
// } | |
// bs_hs, _ := json.Marshal(hs) | |
// b.Log(string(bs_hs)) | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
/* | |
custom dynamic JSON: OPCION 1 | |
*/ | |
// puedo castear Xoo como Zar ? no | |
// pero puedo hacerle un custom JSON | |
type Xoo struct { | |
Height int `json:"height"` | |
Weight int `json:"weight"` | |
} | |
func (x *Xoo) MarshalJSON() ([]byte, error) { | |
if x.Height < 100 { | |
// custom JSON if condition | |
return json.Marshal( | |
&struct { | |
Height int `json:"x"` | |
Weight int `json:"y,omitempty"` | |
// ID int64 `json:"id"` | |
// Name string `json:"name"` | |
// LastSeen int64 `json:"lastSeen"` | |
}{ | |
Height: 123, | |
// ID: int64(x.Height), | |
// Name: "el nombre", | |
// LastSeen: time.Now().Unix(), | |
}, | |
) | |
} | |
// return json.Marshal(x) <- recursion | |
type XooAux Xoo | |
return json.Marshal( | |
&XooAux{ | |
Height: x.Height, | |
Weight: x.Weight, | |
}, | |
) | |
} | |
/* | |
custom dynamic JSON: OPCION 2 | |
*/ | |
// debe tener exactamente los mismos fields | |
type Zar struct { | |
Height int `json:"height"` | |
Weight int `json:"weight,omitempty"` | |
} | |
func (z *Zar) MarshalJSON() ([]byte, error) { | |
// mascara | |
c := z.Weight | |
z.Weight = 0 | |
type ZooAux Zar | |
bs, err := json.Marshal( | |
(*ZooAux)(z), | |
) | |
z.Weight = c | |
return bs, err | |
} | |
/* | |
custom dynamic JSON: OPCION 1 | |
*/ | |
func BenchmarkMarshaler1(b *testing.B) { | |
var xs [n]Xoo | |
// xs := make([]Xoo, n) | |
for i := 0; i < n; i++ { | |
if i%2 == 0 { | |
xs[i] = Xoo{Height: 134, Weight: 666} // 0, 2, 4, ... grandes | |
} else { | |
xs[i] = Xoo{Height: 70, Weight: 50} // 1, 3, 5, ... chicos | |
} | |
} | |
values := make([]*Xoo, n) | |
for i := 0; i < n; i++ { | |
values[i] = &xs[i] | |
} | |
bs_xs, err := json.Marshal(values) | |
if err != nil || len(bs_xs) < 1 { | |
b.Error(err) | |
} | |
// b.Log(string(bs_xs)) | |
} | |
/* | |
custom dynamic JSON: OPCION 2 | |
*/ | |
func BenchmarkMarshaler2(b *testing.B) { | |
var xs [n]Xoo | |
// xs := make([]Xoo, n) | |
for i := 0; i < n; i++ { | |
if i%2 == 0 { | |
xs[i] = Xoo{Height: 134, Weight: 666} // 0, 2, 4, ... grandes | |
} else { | |
xs[i] = Xoo{Height: 70, Weight: 50} // 1, 3, 5, ... chicos | |
} | |
} | |
values := make([]json.Marshaler, n) | |
for i := 0; i < n; i++ { | |
if i%2 == 0 { | |
values[i] = &xs[i] // 0, 2, 4, ... grandes | |
} else { | |
values[i] = (*Zar)(&xs[i]) // 1, 3, 5, ... chicos | |
} | |
} | |
// json.Marshal(values) | |
bs_vals, err := json.Marshal(values) | |
// bs_vals, err := json.Marshal(xs) | |
if err != nil || len(bs_vals) < 1 { | |
b.Error(err) | |
} | |
// b.Log(string(bs_vals), "<--") | |
} | |
// resultados (cuando Xoo tenia int64, string y unix.time) | |
// BenchmarkMarshaler1 14.688s | |
// BenchmarkMarshaler2 10.191s | |
// resultados con mismos campos en ambos structs | |
// BenchmarkMarshaler1 9.880s | |
// BenchmarkMarshaler2 10.288s [+1.04% mas lento] | |
// BenchmarkMarshalAll 2.425s | |
// BenchmarkMarshalMaskPointers1 4.256s | |
// BenchmarkMarshalMaskPointers2 2.667s | |
// BenchmarkMarshalMaskValues 5.409s | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
/* | |
conozco: ID,nom | |
un grupo eso [ID,ID] o [ID,ID,ID] | |
buscando son: <[ID], config> | |
ya sabes que el host no va a estar en buscando porque esta en un grupo. | |
usas el id del host como key. | |
fin. | |
*/ | |
type FooBar interface { | |
run() | |
} | |
type Foo struct { | |
A int `json:"a"` | |
B bool `json:"b"` | |
} | |
func (f Foo) run() { | |
fmt.Println("--") | |
} | |
type Bar struct { | |
D int `json:"d"` | |
E string `json:"e"` | |
} | |
func (b Bar) run() { | |
fmt.Println("!!") | |
} | |
func Test0(t *testing.T) { | |
x := Foo{A: 22, B: true} | |
y := Bar{D: 33, E: "ye"} | |
z := Foo{A: 44, B: false} | |
os := []FooBar{&x, &y, &z} | |
bs, _ := json.Marshal(os) | |
t.Log(string(bs)) | |
} | |
func Test1(t *testing.T) { | |
// eliminar un element de un mapa y pasarlo a otro: | |
var xs, ys map[string]FooBar | |
xs = make(map[string]FooBar) | |
ys = make(map[string]FooBar) | |
xs["primero"] = Foo{A: 1, B: true} | |
xs["segundo"] = Foo{A: 2, B: true} | |
t.Log(xs) | |
// elimino x1 de xs | |
ys["primero"] = xs["primero"] | |
delete(xs, "primero") | |
t.Log("------") | |
t.Log(xs) | |
t.Log(ys) | |
} | |
func Test2(t *testing.T) { | |
a := Foo{A: 100, B: true} | |
b := Foo{A: 101, B: true} | |
bs_a, _ := json.Marshal(a) | |
bs_b, _ := json.Marshal(b) | |
res := [][]byte{bs_a, bs_b} | |
// bs_t := []byte(`,`) + bytes.Join(res, []byte(`,`)) | |
open := []byte(`[`) | |
close := []byte(`]`) | |
cont := bytes.Join(res, []byte(`,`)) | |
res2 := append(open, | |
append( | |
cont, | |
close..., | |
)..., | |
) | |
t.Log(string(res2)) | |
} | |
/* | |
proto.admin.ls <-- ideal | |
padmin.ls <-- muchos paquetes padmin / pcli / plobby / ppplayroom | |
_admin.ls <-- muchos paquetes padmin / pcli / plobby / ppplayroom | |
// nombres feos | |
p.ADMIN_LS | |
p.LOBBY_FORWARD | |
p.ADMIN_Ls | |
p.LOBBY_Forward | |
p.ADMIN_ls | |
p.LOBBY_forward | |
p.admin_LS | |
p.lobby_FORWARD | |
p.admin_Ls | |
p.lobby_Forward | |
p.admin_ls | |
p.lobby_forward | |
p.adminLs | |
p.lobbyForward | |
p.adminls | |
p.lobbyforward | |
*/ | |
// ------------------------- | |
func Test3(t *testing.T) { | |
t.Log("---------test 3---------") | |
/* | |
custom dynamic JSON: OPCION 1 | |
*/ | |
hs := []Xoo{ | |
{Height: 134, Weight: 666}, | |
{Height: 70, Weight: 50}, | |
} | |
bs_hs, _ := json.Marshal(hs) | |
t.Log(string(bs_hs)) | |
} | |
func Test4(t *testing.T) { | |
t.Log("---------test 4---------") | |
/* | |
custom dynamic JSON: OPCION 1 | |
*/ | |
hs := []Xoo{ | |
{Height: 134, Weight: 666}, | |
{Height: 70, Weight: 50}, | |
} | |
bs_hs, _ := json.Marshal(hs) | |
t.Log(string(bs_hs)) | |
} | |
/* | |
custom dynamic JSON: OPCION 2 | |
*/ | |
func Test5(t *testing.T) { | |
t.Log("---------test 5---------") | |
hs := []Xoo{ | |
{Height: 134, Weight: 666}, | |
{Height: 70, Weight: 50}, | |
} | |
// type conversion (*new_type)(x) : | |
// https://stackoverflow.com/questions/43176625/call-json-unmarshal-inside-unm | |
// arshaljson-function-without-causing-stack-overflow | |
var hs2 []json.Marshaler = []json.Marshaler{ | |
&hs[0], | |
(*Zar)(&hs[1]), | |
} | |
// t.Log((Zar)(hs[0])) | |
bs_hs2, _ := json.Marshal(hs2) | |
t.Log(string(bs_hs2)) | |
} | |
func Test6(t *testing.T) { | |
t.Log("---------test 6---------") | |
const n = 4 | |
var xs2 [n]Xoo | |
for i := 0; i < n; i++ { | |
if i%2 == 0 { | |
xs2[i] = Xoo{Height: 134, Weight: 666} | |
} else { | |
xs2[i] = Xoo{Height: 70, Weight: 50} | |
} | |
} | |
values := make([]json.Marshaler, n) | |
for i := 0; i < n; i++ { | |
if i%2 == 0 { | |
values[i] = &xs2[i] | |
} else { | |
values[i] = (*Zar)(&xs2[i]) | |
} | |
} | |
bs, err := json.Marshal(values) | |
if err != nil { | |
t.Log(err) | |
} else { | |
t.Log(len(bs)) | |
// fmt.Println(string(bs)) | |
} | |
} | |
func Test7(t *testing.T) { | |
t.Log("---------test 7---------") | |
hs11 := [2]Xoo{ | |
{Height: 134, Weight: 666}, | |
{Height: 70, Weight: 50}, | |
} | |
bs_hs11, _ := json.Marshal(hs11) | |
t.Log(string(bs_hs11)) | |
bs_hs11b, _ := json.Marshal(hs11[:]) | |
t.Log(string(bs_hs11b)) | |
hs22 := []Xoo{ | |
{Height: 134, Weight: 666}, | |
{Height: 70, Weight: 50}, | |
} | |
bs_hs22, _ := json.Marshal(hs22) | |
t.Log(string(bs_hs22)) | |
} | |
func BenchmarkMask1(b *testing.B) { | |
// quiero que una lista de elementos PersonaPrivada | |
// la renderice como PersonaPrivada para admines y comom PersonaPublica | |
// para el resto | |
type Ficha struct { | |
Foo uint64 `json:"foo"` | |
Bar float64 `json:"bar,omitempty"` // <--- priv!! | |
Baz []string `json:"baz"` | |
} | |
type PersonaPrivada struct { | |
Age int `json:"age"` | |
Weight int `json:"weight,omitempty"` // <--- priv!! | |
Family []string `json:"family"` | |
Ficha `json:"ficha"` | |
} | |
for i := 0; i < n; i++ { | |
priv := PersonaPrivada{ | |
99, | |
200, | |
[]string{"pepe", "juan", "miguel"}, | |
Ficha{ | |
132, | |
4.5556, | |
[]string{"gonzalez", "martinez", "perez"}, | |
}, | |
} | |
priv.Age += 2 | |
bs_priv, _ := json.Marshal(priv) | |
// respaldo | |
bar := priv.Bar | |
w := priv.Weight | |
// enmascaro lo que no quiero | |
priv.Bar = 0 | |
priv.Weight = 0 | |
bs_pub, _ := json.Marshal(priv) | |
// restauro | |
priv.Bar = bar | |
priv.Weight = w | |
if len(bs_priv)+len(bs_pub) < 2 { | |
b.Error("omar algo anda mal") | |
} | |
} | |
// t.Log(string(bs_priv)) | |
// t.Log(string(bs_pub)) | |
} | |
func BenchmarkMask2(b *testing.B) { | |
// quiero que una lista de elementos PersonaPrivada | |
// la renderice como PersonaPrivada para admines y comom PersonaPublica | |
// para el resto | |
type PersonaPublica struct { | |
Age int `json:"age"` | |
Family []string `json:"family"` | |
Ficha struct { | |
Foo uint64 `json:"foo"` | |
Baz []string `json:"baz"` | |
} | |
} | |
type Ficha struct { | |
Foo uint64 `json:"foo"` | |
Bar float64 `json:"bar"` // <--- priv!! | |
Baz []string `json:"baz"` | |
} | |
type PersonaPrivada struct { | |
Age int `json:"age"` | |
Weight int `json:"weight"` // <--- priv!! | |
Family []string `json:"family"` | |
Ficha `json:"ficha"` | |
} | |
for i := 0; i < n; i++ { | |
priv := PersonaPrivada{ | |
99, | |
200, | |
[]string{"pepe", "juan", "miguel"}, | |
Ficha{ | |
132, | |
4.5556, | |
[]string{"gonzalez", "martinez", "perez"}, | |
}, | |
} | |
priv.Age += 2 | |
pub := PersonaPublica{ | |
Age: priv.Age, | |
Family: priv.Family, | |
Ficha: struct { | |
Foo uint64 `json:"foo"` | |
Baz []string `json:"baz"` | |
}{ | |
Foo: priv.Ficha.Foo, | |
Baz: priv.Ficha.Baz, | |
}, | |
} | |
bs_priv, _ := json.Marshal(priv) | |
bs_pub, _ := json.Marshal(pub) | |
if len(bs_priv)+len(bs_pub) < 2 { | |
b.Error("omar algo anda mal") | |
} | |
} | |
// t.Log(string(bs_priv)) | |
// t.Log(string(bs_pub)) | |
} | |
/* | |
➜ prueba go test -bench=BenchmarkMask1 | |
ok example.com/m 10.065s | |
➜ prueba go test -bench=BenchmarkMask2 | |
ok example.com/m 8.616s | |
➜ prueba go test -bench=BenchmarkMask1 | |
ok example.com/m 36.985s | |
➜ prueba go test -bench=BenchmarkMask2 | |
ok example.com/m 36.754s | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment