Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: memory and performance degradation #12640

Open
dvyukov opened this issue Sep 16, 2015 · 6 comments
Open

runtime: memory and performance degradation #12640

dvyukov opened this issue Sep 16, 2015 · 6 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime.
Milestone

Comments

@dvyukov
Copy link
Member

dvyukov commented Sep 16, 2015

Below is the program, I am running it with 1.4 and current tip. There are significant regressions with binary size, execution time and memory consumption.
Binary size on 1.4 3581368, binary size on tip is 4096280.
Below are results of running it with TIME="%e %M" time:

1.4
4.04 3035492
4.38 3035496
4.66 3035500
4.51 3035500
4.42 3035504
4.34 3035500
4.20 3035496
3.87 3035496
4.07 3035496
4.15 3035504
4.28 3035492

tip
4.93 3009044
5.30 4910668
5.49 2978652
5.05 3929244
5.86 2980032
5.91 3929368
5.24 2980052
5.26 3929196
5.64 2980976
5.60 2979944
5.15 3929228
5.36 3929224
4.97 2980096
6.36 3929472
4.77 3929172

1.4 reliably consumes 3GB, while 1.5 can consume 3GB or 4GB or 5GB.
There also seems to be a performance regression of about 30%.

Memory consumption instability and variance seems to be the most troublesome.
tip should not consume significantly more than 1.4.

package gob

import (
    "bytes"
    "encoding/gob"
    "fmt"
    "io"
    "reflect"
    "testing"

    "github.com/dvyukov/go-fuzz/examples/fuzz"
)

type X struct {
    A int
    B string
    C float64
    D []byte
    E interface{}
    F complex128
    G []interface{}
    H *int
    I **int
    J *X
    K map[string]int
}

func init() {
    gob.Register(X{})
}

func TestT(t *testing.T) {
    data :=     "#\xff\x99\x03\x01\x01\x03RT\x19\x01\xff\x9a\x00\x01\xfb\x00\aA\x01" +
    "\x04\x00\x01\x01B\x01\f\x00\x01\x01C\x01\b\x00\x00\x00\x16\xff\x9a\x01" +
    "\"\x01\x05hello\x01\u007f\xff\xff\xff\xf0\xf9!\t@\x00"

    Fuzz([]byte(data))
}

func Fuzz(data []byte) int {
    score := 0
    for _, ctor := range []func() interface{}{
        func() interface{} { return nil },
        func() interface{} { return new(int) },
        func() interface{} { return new(string) },
        func() interface{} { return new(float64) },
        func() interface{} { return new([]byte) },
        func() interface{} { return new(interface{}) },
        func() interface{} { return new(complex128) },
        func() interface{} { m := make(map[int]int); return &m },
        func() interface{} { m := make(map[string]interface{}); return &m },
        func() interface{} { return new(X) },
    } {
        v := ctor()
        dec := gob.NewDecoder(bytes.NewReader(data))
        if dec.Decode(v) != nil {
            continue
        }
        dec.Decode(ctor())
        score = 1
        if ctor() == nil {
            continue
        }
        b1 := new(bytes.Buffer)
        if err := gob.NewEncoder(b1).Encode(v); err != nil {
            panic(err)
        }
        v1 := reflect.ValueOf(ctor())
        err := gob.NewDecoder(bytes.NewReader(data)).DecodeValue(v1)
        if err != nil {
            panic(err)
        }
        if !fuzz.DeepEqual(v, v1.Interface()) {
            fmt.Printf("v0: %#v\n", reflect.ValueOf(v).Elem().Interface())
            fmt.Printf("v1: %#v\n", v1.Elem().Interface())
            panic(fmt.Sprintf("values not equal %T", v))
        }
        b2 := new(bytes.Buffer)
        err = gob.NewEncoder(b2).EncodeValue(v1)
        if err != nil {
            panic(err)
        }
        v2 := ctor()
        dec1 := gob.NewDecoder(b1)
        if err := dec1.Decode(v2); err != nil {
            panic(err)
        }
        if err := dec1.Decode(ctor()); err != io.EOF {
            panic(err)
        }
        if vv, ok := v.(*X); ok {
            fix(vv)
        }
        if !fuzz.DeepEqual(v, v2) {
            fmt.Printf("v0: %#v\n", reflect.ValueOf(v).Elem().Interface())
            fmt.Printf("v2: %#v\n", reflect.ValueOf(v2).Elem().Interface())
            panic(fmt.Sprintf("values not equal 2 %T", v))
        }
    }
    return score
}

func fix(vv *X) {
    // See https://github.com/golang/go/issues/11119
    if vv.I != nil && (*vv.I == nil || **vv.I == 0) {
        // If input contains "I:42 I:null", then I will be in this weird state.
        // It is effectively nil, but DeepEqual does not handle such case.
        vv.I = nil
    }
    if vv.H != nil && *vv.H == 0 {
        vv.H = nil
    }
    if vv.J != nil {
        fix(vv.J)
    }
}

go version devel +a1aafdb Tue Sep 15 16:12:59 2015 +0000 linux/amd64

@dvyukov
Copy link
Member Author

dvyukov commented Sep 16, 2015

@ianlancetaylor ianlancetaylor added this to the Go1.6 milestone Sep 16, 2015
@rsc
Copy link
Contributor

rsc commented Nov 24, 2015

We're not going to get to this for Go 1.6.

@rsc rsc modified the milestones: Unplanned, Go1.6 Nov 24, 2015
@aclements aclements self-assigned this Nov 24, 2015
@aclements
Copy link
Member

The memory instability appears to be fixed at tip. There's still a performance regression and the binaries are still bigger.

1.4
4.53 3036356
4.55 3036348
4.66 3036348
4.50 3036348
4.49 3036348

tip (bea9ae2 linux/amd64)
7.76 2947704
8.76 2947704
7.93 2947700
8.43 2947748
7.32 2947688

@nishantroy
Copy link

@dvyukov can you still repro this issue with 1.10?

@dvyukov
Copy link
Member Author

dvyukov commented Apr 19, 2018

I don't have time right now. But anybody is free to try, there is a repro.

@agnivade
Copy link
Contributor

Results with 1.13beta1 (linux/amd64)-

for i in {1..10}; do TIME="%e %M" time go1.13beta1 test -run=TestT; done

3.94 3930436
3.72 1967292
3.12 3930452
3.05 3930480
3.73 2948844
3.17 3009676
3.05 3930480
3.06 3930528
3.70 1967368
3.72 2949060

Performance seems to be fixed but memory usage seems to be jumping around.

@dvyukov @aclements - How do we want to proceed with this ?

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime.
Projects
None yet
Development

No branches or pull requests

7 participants