4 ms·
But go has an imprecise GC (in reference implementation) or stack maps (in gccgo), so the GC overhead is rather huge. It also lacks of compaction, so cache miss
by ernst_klim 8y ago
But go has an imprecise GC (in reference implementation) or stack maps (in gccgo), so the GC overhead is rather huge. It also lacks of compaction, so cache misses are not that good too.
- weberc2 8y agoNot sure what you mean by imprecise, but Go’s GC does trade throughput for latency. The overhead still isn’t huge if only because there is so much less garbage than in other GC languages. I’m also surprised by your cache misses claim; Go has value types which are used extensively in idiomatic code so generally the cache properties seem quite good—maybe my experience is abnormal?
- ernst_klim 8y ago>Not sure what you mean by imprecise It's a rigid term: https://en.wikipedia.org/wiki/Tracing_garbage_collection#Precise_vs._conservative_and_internal_pointers https://en.wikipedia.org/wiki/Tracing_garbage_collection#Pre... perf shows how much time does GC eat, and that's quite a lot. Thus in the majority of benchmarks go lags behind java or on par with it at best. >there is so much less garbage than in other GC languages That is not true since strings and interfaces are heap allocated thus the only stack allocated objects are numbers and very simple structs (i.e. which contains only numbers), so you would have a lot of garbage unless you are doing a number crunching, which could be easily optimized by inlining and register allocation anyway.
- weberc2 8y ago> It's a rigid term Ah, neat! I learned something. :) You’re mistaken about only numbers and simple structs being stack allocated. All structs are stack allocated unless they escape, regardless of their contents. Further, arrays and constant-sized slices may also be stack allocated. I’m also pretty sure interfaces are only heap allocated if they escape; in other words, if you put a value in an interface and it doesn’t escape, there shouldn’t be an allocation at all.
- ernst_klim 8y agoBoth arrays and interfaces are heap allocated. Slice is just a pointer to a heap allocated array. Structure could be stack allocated, but any of it's fields would not if there is anything but a number. A trivial example: https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/ https://segment.com/blog/allocation-efficiency-in-high-perfo... func main() { x := 42 fmt.Println(x) } ./main.go:7: x escapes to heap So a trivial interface cast leads to allocation.
- weberc2 8y agoLooks like you're right about interfaces (full benchmark source code: https://gist.github.com/weberc2/87d2fdc379065a2765d1c9f490ad8f9e https://gist.github.com/weberc2/87d2fdc379065a2765d1c9f490ad...)! BenchmarkEscapeInterface-4 50000000 33.3 ns/op 8 B/op 1 allocs/op BenchmarkEscapeConcreteValue-4 200000000 9.45 ns/op 0 B/op 0 allocs/op BenchmarkEscapeConcretePointer-4 100000000 10.0 ns/op 0 B/op 0 allocs/op But arrays are stack allocated: BenchmarkEscapeArray-4 50000000 21.3 ns/op 0 B/op 0 allocs/op And structs are stack allocated, as are their fields--even fields that are structs, slices, and strings!: BenchmarkEscapeStruct-4 100000000 12.8 ns/op 0 B/op 0 allocs/op The code: type Inner struct { Slice []int String string Int int } type Struct struct { Int int String string Nested Inner } func (s Struct) AddThings() int { return s.Int + len(s.String) + len(s.Nested.Slice) + len(s.Nested.String) + s.Nested.Int } func BenchmarkEscapeStruct(b *testing.B) { for i := 0; i < b.N; i++ { s := Struct{ Int: 42, String: "Hello", Nested: Inner{ Slice: []int{0, 1, 2}, String: "World!", Int: 42, }, } _ = s.AddThings() } }
- ernst_klim 8y agoI'm sure your strings are not stack allocated, they are statically allocated (and would be statically alocated in any language). Not sure about arrays, but dynamic arrays should be dynamically allocated do, your arrays are static probably. They would be heap allocated, if you would use make.