How to decide transfer pointer or original struct to channel?

11,252 views
Skip to first unread message

dlin

unread,
May 15, 2013, 10:35:39 PM5/15/13
to golan...@googlegroups.com
In this tutorial code
http://golang.org/doc/codewalk/sharemem/

type State struct {
	url    string
	status string
}
type Resource struct {
	url      string
	errCount int
}
func Poller(in <-chan *Resource, out chan<- *Resource, status chan<- State) {}

The State is transfer by full structure, but the Resource transfer by pointer.
Why State is designed as full structure transfer?
Is there any criteria?

In C we often transfer pointer of structure to let it faster.

Tad Glines

unread,
May 15, 2013, 11:00:05 PM5/15/13
to dlin, golang-nuts
Methods are generally written against a pointer receiver (e.g. func (t *type) Method() {}, instead of func (t type) Method() {}) because when the receiver isn't a pointer type the entire value is copied when the method is called.

This is why *Resource is passed down the channel instead of Resource.

By sending State (instead of *State) down a channel, you avoid an additional allocation and subsequent GC. State is small (basically 2 pointers). If State where larger, it might make more sense to pass it around by reference instead of copying.

There is probably existing golang documentation that says all this much better. I just don't know where it is.



--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Kevin Gillette

unread,
May 15, 2013, 11:35:58 PM5/15/13
to golan...@googlegroups.com, dlin
On Wednesday, May 15, 2013 9:00:05 PM UTC-6, Tad Glines wrote:
Methods are generally written against a pointer receiver (e.g. func (t *type) Method() {}, instead of func (t type) Method() {}) because when the receiver isn't a pointer type the entire value is copied when the method is called.

Methods are generally written against a pointer receiver if the type has any methods that need to mutate the value.  Certainly you could write read-only methods using a value receiver, and mutating methods using a pointer receiver, but that makes it less clear to users of the code how the type is intended to be used. Therefore, the convention is that if any methods need to have mutating semantics, then all methods should use a pointer receiver, unless there are special circumstances (i.e. a "good reason") for having mixed value and pointer receivers on the same type.  The issue of copying or not copying the underlying value should be the last consideration. 
 
This is why *Resource is passed down the channel instead of Resource.

Unless (and even if) all mutating methods are unexported, there can be a lot of benefit to passing a copy of data through a channel. The "share memory by communicating" motto has an implication on communicating shared memory -- to use it safely in a concurrent fashion, you either need to take away or limit concurrency, or by communicating even more. Limiting concurrency is simpler, and is conventionally achieved either by passing a value copy (in which case you're not actually sharing memory), or by avoiding use of a pointer after you send it down a channel, in which case the sender can't/shouldn't use that memory until they receive the pointer again.
 
By sending State (instead of *State) down a channel, you avoid an additional allocation and subsequent GC. State is small (basically 2 pointers). If State where larger, it might make more sense to pass it around by reference instead of copying.

Based on the OP's code, neither sending State nor *State would involve any allocations. Regardless of platform, all pointers and values of type int, uint, or uintptr are "word sized", and string values (not the data they reference) consist of a pointer and an int. Therefore, State is always a 4-word structure.
 
There is probably existing golang documentation that says all this much better. I just don't know where it is.
 
In the codewalk example, Resource is passed by pointer because it has mutating methods. If it did not, the code would likely send value copies through the channels.

On Wed, May 15, 2013 at 7:35 PM, dlin <dli...@gmail.com> wrote:
In C we often transfer pointer of structure to let it faster.

You may be surprised how large a struct can get before passing it has a noticeable performance impact compared to passing a pointer to that struct (not to mention accessing data behind a pointer involves an indirection, and especially when the data is shared across processors, there can be additional cost. Unless you have already empirically studied and weighed the costs of each approach, I'd recommend choosing a design that makes the most semantic sense, and if the right semantic choice is value passing and only if profiling shows that value passing is too expensive for a given use-case should you then redesign the code to pass pointers.

Compared to the overhead of channel communication across multiple processors, while itself not particularly slow, makes the difference in cost between pointer and value-copies-of-reasonable-size negligible. In other words, choosing pointers over values-of-reasonable-size solely for performance reasons when dealing with channels is essentially a pointless micro-optimization (unless you have tangible profiling data to prove otherwise).
Reply all
Reply to author
Forward
0 new messages