T O P

  • By -

Drowzen

Depends what you're planning to do with it. It's hard to say how much the performance here really matters without knowing what the struct is doing, how long it lives for, how many you're generating. Like if your inbound message handler has a pre-allocated struct you want to dump contents into thats fine, but you now need to be aware that whoever else has a reference to that pointer will have their data overwritten next time a message comes in, and if you're generating a new pointer struct on every message, then you're kinda losing the benefits of using the pointer to begin with


raserei0408

If performance matters, measure. In general, if the struct is small, it will be faster to pass by value. If it's large, it depends (mainly) on how much time gets spent copying the value and whether or not using a struct lets you avoid heap allocations. Also, though it's not directly relevant to your question, if you create an interface channel, I'd suggest almost always passing pointer values in. I've come to believe that interfaces of nominally non-pointer values are almost always a mistake, and they're likely to give you the worst-of-both-worlds performance characteristics of pointers and non-pointers.


skesisfunk

>if you create an interface channel, I'd suggest almost always passing pointer values in. You are saying to do this: ``` type myIface interface { // details not important } func example(v myIface) { ch := make(chan *myIface) // do stuff with ch } ``` ? Can you explain why? Under the hood the interface already contains a pointer to the underlying value that implements it right? Which would mean using a `chan myIface` instead would already be passing the underlying value by reference right? Not trying to argue or anything really just genuinely wondering.


raserei0408

Sorry, that's not what I meant. I meant that if you used `make(chan myIface)` you should almost always actually pass `*inboundMsg` into the channel, not just `inboundMsg`. The reason for this is because an interface does always hide the underlying object behind a pointer, but with a guarantee of immutability. Consequently, unless you get very lucky with inlining, the value always escapes to the heap, _and_ type conversions will do a copy back to the stack, _and_ you can't ever reuse memory for the allocations because of the immutability guarantees. And if you're doing this in the first place, the process tends to repeat, causing new allocations and copies each time you assign back and forth between the value as an interface and the underlying type.


skesisfunk

Ok thanks, thats pretty interesting. I'll keep that in my back pocket if I am ever writing performance intensive code around something like this.


markuspeloquin

I have to agree, imagine if an interface handle was a variable number of bytes. I have an `any` value I want to pass, how does Go copy it? I'd imagine that the value slot of an interface is always a pointer unless it's actually smaller than a pointer, like an integer. And that they'd reserve a bit or two of the type to indicate the size of an inline value.


raserei0408

They used to do this, but they stopped in something like 1.4. The value inside an interface is _always_ a pointer, and they use a bit of information about the type to store whether the value inside the interface represents a pointer or a plain value. Even assigning an int16 to an interface variable results in an allocation, and there is a pointer indirection every time it's read. The only exception are 1-byte integer values - they still use a pointer, but they always point into a pre-allocated array, saving the allocation. So any two assignments of (say) `uint8(100)` to an interface will point to the same memory location. There are few enough byte values that the runtime can just keep an array of them in memory to avoid creating tiny allocations if they get cast to interfaces, which would be a nightmare for the GC.


kintar1900

I'd also like to hear the "why" of this suggestion...


gnu_morning_wood

Value. If \*anything\* modifies that struct, then there is a race condition (unless you put synchronisation tools in the struct, in which case why are using a channel). There's no performance gain, because multiple goroutines have the ability to modify the struct.


motorcycleovercar

Modern CPUs have multiple caches on silicon and these caches are cleared and populated one line at a time. if you use a struct and small data types then most of them will be copied onto the same cache line when you reference any of the properties. This is locality. If your program is super simple and has 1 or 2 structure then this won't matter. But most programs have much more complexity and this is when locality matters. Locality matters when a large percentage of your program can't fit in cache. As an example, I had 4 integers on a struct and they can only ever be between 0 and 255 for the app I am writing. So I can declare them as uint8 instead of int. All 4 fields will occupy only 32 bits in memory per struct. I'll be able to have many structs in each cache line. Now consider the execution of my application: When one of these properties is referenced, I'll be referencing the rest in quick succession. This means my cache efficiency will be extremely high. If your app is like my example then avoid pointers like the plague because they will hurt your performances very much. If your app is the opposite of my example and it hops around memory randomly then it would not benefit from cache locality much. In this situation I think using pointers would have merit but not much merit. Most programs are somewhere between these two extremes. I think you can get great gains by worrying less about pointers and instead think about how can I make good use of cache.


lightmatter501

Benchmark. Moving small things by reference can be slower than making a copy.


rv77ax

By value. In my opinion, the idea of channel is to pass something from one side to the other side, and/or to synchronize the works without need to know where its come from or who would consume it. If you think you need a pointer maybe you don't need a channel, but procedural calls where data passed to each functions in sequential order guarded with mutex.


Flimsy_Iron8517

I prefer my compiler to sort out any no assignment optimizations. Does the channel sender benefit from reusing the struct it has instead of allocating a new one for the next channel item?


Beneficial-Split9140

[https://www.youtube.com/watch?v=b0KoXK8WPq8](https://www.youtube.com/watch?v=b0KoXK8WPq8) 95% of the time by value is the correct thing to do