go version)?$ go version go version go1.14.2 linux/amd64
yes
go env)?go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/user/.cache/go-build"
GOENV="/home/user/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/user"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/user/src/golang.org/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/user/src/golang.org/go/pkg/tool/linux_amd64"
GCCGO="/usr/bin/gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build436707887=/tmp/go-build -gno-record-gcc-switches"
https://play.golang.org/p/lNId1c5z_Dk
cat <<EOF > hello.go
package main
import (
"fmt"
"io"
)
type Bar interface {
Bark()
Meow()
}
type Foo struct {
}
func (*Foo) Bark() {
fmt.Println("Bark")
}
func (*Foo) Meow() {
fmt.Println("Meow")
}
func baz(v Bar) {
v.Bark()
}
func main() {
x := &Foo{}
baz(x)
}
EOF
$ go build hello.go
$ nm hello | grep Meow
00000000004915c0 T main.(*Foo).Meow
00000000004da690 R main.(*Foo).Meow.stkobj
md5-f9ac1a26c9d6f951c0e26bdf110930ae
nm hello | grep Meow
md5-c2479f6d4fcde6c7496fe18c98f4874c
$ nm hello | grep Meow
00000000004915c0 T main.(*Foo).Meow
00000000004da690 R main.(*Foo).Meow.stkobj
Gccgo has the remaining Meow too. But tinygo successfully removes the method.
Related to https://github.com/golang/go/issues/6853 and https://github.com/golang/go/issues/36313
Also found https://aykevl.nl/2018/12/tinygo-interface, which might be of interest.
We probably could do that. I'm thinking on reworking the linker's live method analysis. I'll keep this in mind.
I think the compiler needs to emit some kind of marker at Bar.Bark interface call, so the linker knows Bark is used. The linker then could track only marked interface methods, instead of all of them as we are doing today.
The question is how frequently this will fire, i.e. how many methods we can eliminate by doing this.
But, most importantly, as a cat person, I oppose removing the Meow method regardless.
Also for the following example, I expect the Fish's Meow and Bark should be removed.
package main
import (
"fmt"
)
type Bar interface {
Bark()
Meow()
}
type Foo struct {
}
func (*Foo) Bar() {
fmt.Println("Bar")
}
//go:noinline
func (f *Foo) Bark() {
fmt.Println("Meow")
}
//go:noinline
func (*Foo) Meow() {
fmt.Println("Meow")
}
type Fish struct {
}
//go:noinline
func (*Fish) Bark() {
fmt.Println("Fish Bark")
}
//go:noinline
func (*Fish) Meow() {
fmt.Println("Fish Meow")
}
func baz(v Bar) {
v.Bark()
}
func main() {
x := &Foo{}
y := &Fish{}
baz(x)
fmt.Println(x)
fmt.Println(y)
}
$ nm hello | grep Fish
0000000000491650 T main.(*Fish).Bark
00000000004da760 R main.(*Fish).Bark.stkobj
00000000004916e0 T main.(*Fish).Meow
00000000004da780 R main.(*Fish).Meow.stkobj
We at u-root are very interested in this.
DCE that eliminates exported non-interface methods saves about 12% for us (and when it stops working our binaries no longer fit into flash chips which kinda sucks).
I expect eliminating unused interface methods to bring similar improvement.
@cherrymui If you can put together a patch I will be happy to test it to give you some numbers.
@rojer Have you tried the tip version of Go? I made some improvements in the dev.link branch which is merged a few days ago. Does that make any difference? Thanks.
@cherrymui looks like there's been a minor improvement since 1.15:
[rojer@nbd ~/go/src/github.com/u-root/u-root master]$ for GO in ~/go/go1.14.7/bin/go ~/go/go1.15/bin/go ~/go/go-git/bin/go; do $GO version; $GO run github.com/u-root/u-root/tools/makebb -o /tmp/bb cmds/*/* > /dev/null 2>&1; ls -la /tmp/bb; done
go version go1.14.7 linux/amd64
-rwxrwxr-x 1 rojer rojer 17956864 Aug 19 20:11 /tmp/bb
go version go1.15 linux/amd64
-rwxrwxr-x 1 rojer rojer 14299136 Aug 19 20:11 /tmp/bb
go version devel +64350f1eab Wed Aug 19 17:54:18 2020 +0000 linux/amd64
-rwxrwxr-x 1 rojer rojer 14127104 Aug 19 20:11 /tmp/bb
big step down from 1.14 is the unused methods DCE: 1.15 shipped with the fix for https://github.com/golang/go/issues/36021
This looks to be fixed on tip:
$ go build barkmeow.go
$ nm barkmeow | fgrep Meow
$
Marking this bug closed -- please reopen if you disagree. Thanks.
doesn't seem to work across package boundaries, UnusedInterfaceMethod doesn't get eliminated:
[rojer@nbd /tmp/foobar/foo]$ GOROOT=/home/rojer/go/go-git /home/rojer/go/go-git/bin/go build
[rojer@nbd /tmp/foobar/foo]$ go tool nm foo | grep foobar/bar
497080 T foobar/bar.(*Bar).UnusedInterfaceMethod
497140 T foobar/bar.(*Bar).UsedInterfaceMethod
535e20 D foobar/bar..inittask
496fe0 T foobar/bar.Bar.UsedInterfaceMethod
4da288 R go.itab.foobar/bar.Bar,foobar/bar.Interface
source:
[rojer@nbd /tmp]$ find foobar/
foobar/
foobar/go.mod
foobar/foo
foobar/foo/foo.go
foobar/bar
foobar/bar/bar.go
[rojer@nbd /tmp]$ cat foobar/foo/foo.go
package main
import (
"foobar/bar"
)
func main() {
// Test unused method elimination.
var b bar.Interface
b = bar.Bar{}
b.UsedInterfaceMethod()
}
[rojer@nbd /tmp]$ cat foobar/bar/bar.go
package bar
import "fmt"
type Interface interface {
UsedInterfaceMethod()
UnusedInterfaceMethod()
}
type Bar struct{}
func (Bar) UsedInterfaceMethod() {
fmt.Println("one")
}
// This method is unused but cannot be eliminated yet.
// https://github.com/golang/go/issues/38685
func (Bar) UnusedInterfaceMethod() {
fmt.Println("two")
}
// This method is unused and should be eliminated.
func (Bar) UnusedNonInterfaceMethod() {
fmt.Println("three")
}
@thanm should i file a new issue for this?
Yes, please file a new issue. There are many different flavors/permutations/combinations when it comes to unused method removal; the details matter.
@thanm #42421
Most helpful comment
We probably could do that. I'm thinking on reworking the linker's live method analysis. I'll keep this in mind.
I think the compiler needs to emit some kind of marker at
Bar.Barkinterface call, so the linker knowsBarkis used. The linker then could track only marked interface methods, instead of all of them as we are doing today.The question is how frequently this will fire, i.e. how many methods we can eliminate by doing this.
But, most importantly, as a cat person, I oppose removing the
Meowmethod regardless.