So the context here is that this is about 10% of the whole >30s runtime of the benchmark. I guess your position builder gets called for every token, and the rest is not expensive enough to dwarf the pointer chasing and memcopy here to more than 90%.
I expect the four lines get merged to one copy, which is why the profiler just sees it as the first line.
If I understood it correctly, Golang allocates memory when we use the object for the first time instead of when we are creating it.
I have created positionBuffer slice, see @commit. After that, the function New in PositionPool spends about 6.5s. It is like a sum of createToken and NewTokenPosition functions that most actively created the positions.
After the commit, allocs/op is decreased from 6281 to 4440, but ns/op is increased a little. And now profiler shows that the time spends to growing slice
have found that this behavior documented at allocation_new
new(T) does not initialize the memory, it only zeros it
The article above explains to me why profiler shows so weird timing.
At scanner/lexer.go:488 we creating a new zeroed storage for position.Position and returns its address, it is quick. After that at scanner/lexer.go:538 and parser/position_builder.go:110 we assign data to zeroed object and Go starts memory initialization, it is slow.
I have tried to change scanner/lexer.go:488 to force initialize memory, see gist