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: map of struct with tagged fields is scanned #33973

Closed
minaevmike opened this issue Aug 30, 2019 · 7 comments
Closed

runtime: map of struct with tagged fields is scanned #33973

minaevmike opened this issue Aug 30, 2019 · 7 comments

Comments

@minaevmike
Copy link
Contributor

@minaevmike minaevmike commented Aug 30, 2019

What version of Go are you using (go version)?

$ go version
go version go1.12.9 darwin/amd64

Does this issue reproduce with the latest release?

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/mminaev/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/mminaev/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
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 -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/xc/jlyfzvdn3rx5t_r3p7h3w3vh0000gp/T/go-build181555368=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

following the current example https://github.com/golang/go/wiki/CompilerOptimizations#non-scannable-objects i create huge map with 15000000 elements like this

type ItemPricing struct {
	ItemID        int64 `db:"item_id"`
	BasePrice     int64 `db:"base_price"`
	DiscountPrice int64 `db:"discount_price"`
}

func main() {
	m := make(map[int64]ItemPricing, 15e6)
        ...
}

after this i see increasing in gc working duration

I don't know what is the proper way to check is object scannable or not, but i found such code in runtime/malloc.go
I am not common with cmd/compile package, but seems this function responsible for determining does type has pointers or not cmd/compile/internal/types/type.go

Seems map would be always full-scanned, i am not sure is it bug or this example no longer valid.

@minaevmike minaevmike changed the title question: runtime: does non scannable objects works question: runtime: does non scannable objects works on maps Aug 30, 2019
@minaevmike minaevmike changed the title question: runtime: does non scannable objects works on maps question: runtime: does non scannable objects works with maps Aug 30, 2019
@minaevmike minaevmike changed the title question: runtime: does non scannable objects works with maps question: runtime: does non scannable objects work with maps Aug 30, 2019
@ALTree

This comment has been minimized.

Copy link
Member

@ALTree ALTree commented Aug 30, 2019

Does this still happen if you remove the struct tags? IIRC they're stored as strings. Note that the wiki example has a struct with no tags.

@ALTree

This comment has been minimized.

Copy link
Member

@ALTree ALTree commented Aug 30, 2019

(This is more of a question but I'm leaving this open in case it turns out to be a GC regression).

@ALTree ALTree changed the title question: runtime: does non scannable objects work with maps runtime: map of struct with tagged field is scanned Aug 30, 2019
@ALTree ALTree changed the title runtime: map of struct with tagged field is scanned runtime: map of struct with tagged fields is scanned Aug 30, 2019
@ivanovaleksey

This comment has been minimized.

Copy link

@ivanovaleksey ivanovaleksey commented Aug 30, 2019

@ALTree thanks for the reply. I will try to remove struct tags and check once again.
Meanwhile I have a new question: maybe it is due to map size?
The graph above is from production environment where map contains ~16.2KK elements.
On staging environment map contains ~6.5KK elements and we have GC max time about 10ms.

UPD: well, we actually also had max time on staging about 1s but this morning it suddenly dropped down to 500ms and then to 10-20 ms.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Aug 30, 2019

Struct tags shouldn't make any difference; a struct tag is a string constant that only appears once per type, not once per value.

But, yes, maps always require scanning. I think the wiki page is trying to say that the data stored in the map is not scanned by the GC, and that is true. But the map data structure contains internal pointers, and those have to be scanned.

Closing this issue because it seems to be a question rather than a bug report. Please comment if you disagree. In general you can get better and faster answers to questions on a forum rather than on the issue tracker; see https://golang.org/wiki/Questions. Thanks.

@ALTree

This comment has been minimized.

Copy link
Member

@ALTree ALTree commented Aug 30, 2019

@ianlancetaylor to be honest, if you are right then that wiki page is quite misleading, since when I read:

For example, the following map won't visibly affect GC time:

type Key [64]byte // SHA-512 hash
type Value struct {
	Name      [32]byte
	Balance   uint64
	Timestamp int64
}
m := make(map[Key]Value, 1e8)

I conclude that the map itself won't be scanned... otherwise I don't see how a sentence like "the following map won't visibly affect GC time" before a 1e8 elements map could be true.

@randall77

This comment has been minimized.

Copy link
Contributor

@randall77 randall77 commented Aug 30, 2019

How are you measuring "Go GC duration max"?

You are right that the map itself should not be scanned during GC. But the space it uses does contribute to the live data size. That affects the heap target size. So if there's a map like this that is a significant fraction of your live data, then it will cause your heap to be larger, GC to run less frequently, and GC to take longer when it does run.

@aclements @mknyszek

@ivanovaleksey

This comment has been minimized.

Copy link

@ivanovaleksey ivanovaleksey commented Aug 30, 2019

@randall77 we use Grafana & Prometheus for service monitoring.
Prometheus provides go_gc_duration_seconds metric.
And then we display it as graph in Grafana go_gc_duration_seconds{quantile="1"}.

The application is very simple. Actually it's just in-memory cache over DB.
On startup we scan whole table in DB and create map[int64]ItemPricing as a cache.
Then on some interval we scan once again, create a new map, and replace old map with a new one. Old map is expected to be garbage collected as single object but it seems like there is scanning maps though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can’t perform that action at this time.