go-playground / validator

:100:Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
MIT License
16.64k stars 1.31k forks source link

Bool to struct{} in iso codes #1269

Closed nar10z closed 4 months ago

nar10z commented 4 months ago

Package version eg. v9, v10:

v10

Enhancement:

Reducing memory allocation by changing map[string]bool/map[string]int -> map[string]strcut{}/map[int]struct{} https://gist.github.com/davecheney/3be245c92b61e5045f75

Code sample, to showcase or reproduce:

var iso3166_1_alpha2_eu = map[string]bool{
    "AT": true, "BE": true, "BG": true, "HR": true, "CY": true,
    "CZ": true, "DK": true, "EE": true, "FI": true, "FR": true,
    "DE": true, "GR": true, "HU": true, "IE": true, "IT": true,
    "LV": true, "LT": true, "LU": true, "MT": true, "NL": true,
    "PL": true, "PT": true, "RO": true, "SK": true, "SI": true,
    "ES": true, "SE": true,
}

// Change to:

var iso3166_1_alpha2_eu = map[string]struct{}{
    "AT": {}, "BE": {}, "BG": {}, "HR": {}, "CY": {},
    "CZ": {}, "DK": {}, "EE": {}, "FI": {}, "FR": {},
    "DE": {}, "GR": {}, "HU": {}, "IE": {}, "IT": {},
    "LV": {}, "LT": {}, "LU": {}, "MT": {}, "NL": {},
    "PL": {}, "PT": {}, "RO": {}, "SK": {}, "SI": {},
    "ES": {}, "SE": {},
}

I made a test that simply prints how much memory a particular map occupies

Bool:

//go:build codes_as_bool

package validator

import (
    "fmt"
    "testing"
    "unsafe"
)

func printSize[T comparable](t *testing.T, m map[T]bool) {
    t.Helper()

    size := unsafe.Sizeof(m)
    for k, v := range m {
        size += unsafe.Sizeof(k)
        size += unsafe.Sizeof(v)
    }

    fmt.Printf("Size: %d, bytes: %v\n", len(m), size)
}

func TestIsoCodesSizes(t *testing.T) {
    t.Run("iso3166_1_alpha2", func(t *testing.T) {
        printSize(t, iso3166_1_alpha2)
    })
    t.Run("iso3166_1_alpha2_eu", func(t *testing.T) {
        printSize(t, iso3166_1_alpha2_eu)
    })
    t.Run("iso3166_1_alpha3", func(t *testing.T) {
        printSize(t, iso3166_1_alpha3)
    })
    t.Run("iso3166_1_alpha3_eu", func(t *testing.T) {
        printSize(t, iso3166_1_alpha3_eu)
    })
    t.Run("iso3166_1_alpha_numeric", func(t *testing.T) {
        printSize(t, iso3166_1_alpha_numeric)
    })
    t.Run("iso3166_1_alpha_numeric_eu", func(t *testing.T) {
        printSize(t, iso3166_1_alpha_numeric_eu)
    })
    t.Run("iso3166_2", func(t *testing.T) {
        printSize(t, iso3166_2)
    })
}
=== RUN   TestIsoCodesSizes/iso3166_1_alpha2
Size: 250, bytes: 4258
=== RUN   TestIsoCodesSizes/iso3166_1_alpha2_eu
Size: 27, bytes: 467
=== RUN   TestIsoCodesSizes/iso3166_1_alpha3
Size: 250, bytes: 4258
=== RUN   TestIsoCodesSizes/iso3166_1_alpha3_eu
Size: 27, bytes: 467
=== RUN   TestIsoCodesSizes/iso3166_1_alpha_numeric
Size: 250, bytes: 2258
=== RUN   TestIsoCodesSizes/iso3166_1_alpha_numeric_eu
Size: 27, bytes: 251
=== RUN   TestIsoCodesSizes/iso3166_2
Size: 4987, bytes: 84787

Strcut:

//go:build codes_as_struct

package validator

import (
    "fmt"
    "testing"
    "unsafe"
)

func printSize[T comparable](t *testing.T, m map[T]struct{}) {
    t.Helper()

    size := unsafe.Sizeof(m)
    for k, v := range m {
        size += unsafe.Sizeof(k)
        size += unsafe.Sizeof(v)
    }

    fmt.Printf("Size: %d, bytes: %v\n", len(m), size)
}

func TestIsoCodesSizes(t *testing.T) {
    t.Run("iso3166_1_alpha2", func(t *testing.T) {
        printSize(t, iso3166_1_alpha2)
    })
    t.Run("iso3166_1_alpha2_eu", func(t *testing.T) {
        printSize(t, iso3166_1_alpha2_eu)
    })
    t.Run("iso3166_1_alpha3", func(t *testing.T) {
        printSize(t, iso3166_1_alpha3)
    })
    t.Run("iso3166_1_alpha3_eu", func(t *testing.T) {
        printSize(t, iso3166_1_alpha3_eu)
    })
    t.Run("iso3166_1_alpha_numeric", func(t *testing.T) {
        printSize(t, iso3166_1_alpha_numeric)
    })
    t.Run("iso3166_1_alpha_numeric_eu", func(t *testing.T) {
        printSize(t, iso3166_1_alpha_numeric_eu)
    })
    t.Run("iso3166_2", func(t *testing.T) {
        printSize(t, iso3166_2)
    })
}
=== RUN   TestIsoCodesSizes/iso3166_1_alpha2
Size: 250, bytes: 4008
=== RUN   TestIsoCodesSizes/iso3166_1_alpha2_eu
Size: 27, bytes: 440
=== RUN   TestIsoCodesSizes/iso3166_1_alpha3
Size: 250, bytes: 4008
=== RUN   TestIsoCodesSizes/iso3166_1_alpha3_eu
Size: 27, bytes: 440
=== RUN   TestIsoCodesSizes/iso3166_1_alpha_numeric
Size: 250, bytes: 2008
=== RUN   TestIsoCodesSizes/iso3166_1_alpha_numeric_eu
Size: 27, bytes: 224
=== RUN   TestIsoCodesSizes/iso3166_2
Size: 4987, bytes: 79800

Percentage ~ 7.29%:

iso3166_1_alpha2: 5.87%
iso3166_1_alpha2_eu: 5.78%
iso3166_1_alpha3: 5.87%
iso3166_1_alpha3_eu: 5.78%
iso3166_1_alpha_numeric: 11.07%
iso3166_1_alpha_numeric_eu: 10.76%
iso3166_2: 5.88%