Skip to content

Instantly share code, notes, and snippets.

@filevich
Last active January 30, 2022 13:01
Show Gist options
  • Save filevich/0f26cd5ec986e0b61830231cafc523e2 to your computer and use it in GitHub Desktop.
Save filevich/0f26cd5ec986e0b61830231cafc523e2 to your computer and use it in GitHub Desktop.
golang json slice benchmark
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