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 · 5 comments

Comments

Projects
None yet
5 participants
@dvyukov
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

This comment has been minimized.

Member

dvyukov commented Sep 16, 2015

@ianlancetaylor ianlancetaylor added this to the Go1.6 milestone Sep 16, 2015

@rsc

This comment has been minimized.

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

This comment has been minimized.

Member

aclements commented Dec 13, 2015

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

This comment has been minimized.

nishantroy commented Apr 19, 2018

@dvyukov can you still repro this issue with 1.10?

@dvyukov

This comment has been minimized.

Member

dvyukov commented Apr 19, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment