6 ms·
Go newbie here. Can anybody elaborate on this piece of the docs? """ You might be thinking that it would be more usual to have an ops.Add(ColorOp{Color: red})
by shikaan 2y ago
Go newbie here. Can anybody elaborate on this piece of the docs?
"""
You might be thinking that it would be more usual to have an ops.Add(ColorOp{Color: red}) method instead of using op.ColorOp{Color: red}.Add(ops). It’s like this so that the Add method doesn’t have to take an interface-typed argument, which would often require an allocation to call. This is a key aspect of Gio’s “zero allocation” design.
"""
Why would there be an allocation? Of what? How are we saving it?
- foldr 2y agoOn the first approach ColorOp{Color: red} would have to be boxed and thus heap allocated. This is because ops.Add in general would receive a (fat) pointer to a value implementing a particular interface rather than a value of concrete type. (The sibling comment written at the same time has a more detailed explanation of the 'fat' part.)
- ainar-g 2y agoWhenever you do: v := interfaceType(concreteTypeValue) in Go, what you're actually doing on a lower level is: dataPtr := &concreteTypeValue typePtr := typeData[concreteType]() v := interfaceData{ data: dataPtr, typ: typePtr, } The first line here is the allocation, since (at least, the way I recall the rule) in Go pointers never point to values on the stack, so concreteTypeValue must be allocated on the heap. The rule about pointers not pointing to the stack is there to make it easier for goroutine stacks to grow dynamically. See https://go.dev/doc/faq#stack_or_heap https://go.dev/doc/faq#stack_or_heap.
- foldr 2y ago>in Go pointers never point to values on the stack This is only the case for pointers that the compiler can't prove not to escape: >In the current compilers, if a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.
- ainar-g 2y agoThat could be true, but I've done a few optimizations of allocations in Go code, and I don't recall pointers to stack values ever being optimized (unless the entire branch is removed, of course). If anyone could provide an example of the code that does pointer operations yet doesn't cause allocations, I'd appreciate it!
- foldr 2y agoSure, here you go: https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:go,selection:(endColumn:1,endLineNumber:19,positionColumn:1,positionLineNumber:19,selectionStartColumn:1,selectionStartLineNumber:19,startColumn:1,startLineNumber:19),source:'//+Type+your+code+here,+or+load+an+example.%0A//+Your+function+name+should+start+with+a+capital+letter.%0Apackage+main%0A%0Aimport+%22fmt%22%0A%0A//+example+to+show+what+an+allocation+looks+like+in+the+generated+asm%0Afunc+foo()+*int+%7B%0A++++return+new(int)%0A%7D%0A%0A//+no+allocation+here%0Afunc+main()+%7B%0A++++x+:%3D+7%0A++++px+:%3D+%26x%0A++++*px+%3D+8%0A++++fmt.Printf(%22x:+%25v%5Cn%22,+x)%0A%7D%0A'),l:'5',n:'1',o:'Go+source+%231',t:'0')),k:37.5,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:gl1221,filters:(b:1,binary:'1',binaryObject:'1',commentOnly:1,debugCalls:'1',demangle:'0',directives:1,execute:1,intel:1,libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:go,libs:!(),options:'',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+gc+1.22.1+(Editor+%231)',t:'0')),l:'4',m:50,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(fontScale:14,fontUsePx:'0',j:1,wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gc+1.22.1+(Compiler+%231)',t:'0')),l:'4',m:50,n:'0',o:'',s:0,t:'0')),k:37.5,l:'3',n:'0',o:'',t:'0'),(g:!((h:executor,i:(argsPanelShown:'1',compilationPanelShown:'0',compiler:gl1221,compilerName:'',compilerOutShown:'1',execArgs:'',execStdin:'',fontScale:14,fontUsePx:'0',j:2,lang:go,libs:!(),options:'',overrides:!(),runtimeTools:!(),source:1,stdinPanelShown:'1',wrap:'1'),l:'5',n:'0',o:'Executor+x86-64+gc+1.22.1+(Go,+Editor+%231)',t:'0')),k:25,l:'4',m:100,n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4 https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename...
- 2y ago
- pquki4 2y agohttps://stackoverflow.com/questions/39492539/go-implicit-conversion-to-interface-does-memory-allocation https://stackoverflow.com/questions/39492539/go-implicit-con...
- kevindamm 2y agoThis has to do with how Go handles dynamic typing via interfaces and how structs fit into that. When a function takes an interface type as an argument, and you pass a pure struct to it, it creates a wrapper around it (this is the allocation referred to in your quote) which is simply a pair of pointers, one being a type/vtable pointer and the other being a pointer to the struct's data. Doing it this way allows code to do run-time type inference as well as allow interface extension implicitly (that is, implementing an interface is done by implementing its methods, and you don't have to explicitly type it like ByteReader extends Reader"). Since you only pay this cost if you use it, a lot of fast-path code will use structs exclusively if it can.
- DougBTX 2y agoHow do generics change this? Can a type bound on a generic work like a static interface implementation specification, and recover the cost of wrapping the struct?
- erik_seaberg 2y agoYou can declare generic structs and generic functions, and get monomorphized copies (not erased to "any") of each function for all concrete types used. You can't look them up by reflection. You can't declare generic methods, and still can't overload methods by type.
- paullgdc 2y ago> get monomorphized copies (not erased to "any") of each function for all concrete types used That's not exactly true. You get one concrete function for all generic pointer types for instance. Generic functions take a hidden paramater called a dictionnary, which is fairly similar to vtable, since it contains type information and pointers for all methods of the generic type that the function calls. So methods on generic types are still performed through an indirection. https://github.com/golang/proposal/blob/master/design/generics-implementation-dictionaries-go1.18.md https://github.com/golang/proposal/blob/master/design/generi...
- neonsunset 2y agoHeh. Go just doesn’t let you write zero-cost generic abstractions the way C# does.
- shikaan 2y agoThanks all (:
- Someone 2y ago> You might be thinking that it would be more usual to have an ops.Add(ColorOp{Color: red}) method instead of using op.ColorOp{Color: red}.Add(ops) Others answered your question, but on a different subject: I find that weird to read. To me op.ColorOp{Color: red}.Add(ops) reads like “add ops to the result of op.ColorOp{Color: red}. I would name that function AddTo: op.ColorOp{Color: red}.AddTo(ops) Still unconventional, but at least it messages that the function argument gets modified.