I hope this will help us to better understand
https://medium.com/eureka-engineering/understanding-allocations-in-go-stack-heap-memory-9a2631b5035d


Sharon Mafgaoker – Senior Solutions Architect

M. 050 995 99 16 | sha...@cloud5.co.il




On Fri, 1 Nov 2024 at 19:21 Tushar Rawat <tusharrawat...@gmail.com> wrote:

> Hi Jason,
>
> Make sense.
>
> So ideally even for the types/struct which are *more than 3 bytes* and 
> *immutable,
> *if we are able to prove (with some profiling/perf. test) that their is
> actually a significant performance improvement on replacing value with
> pointer returns (because pointer copy is cheaper due to smaller size), then
> only it makes sense to use pointers, otherwise the type being immutable
> should be good enough reason to use value type, except the cases like
> big-integers where the std library itself suggest to use the pointer types
> for performance gain.
>
>
> On Friday, November 1, 2024 at 9:41:45 PM UTC+5:30 Jason E. Aten wrote:
>
>> Hi Tushar,
>>
>> My rule of thumb in practice is: for returning structs, I almost always
>> return a pointer.
>>
>> The exception is -- the only time I would return a value in general -- is
>> when:
>>
>> a) the returned value is _intended_ to be an immutable value, like a
>> time.Time and a string value;
>>
>> *and*
>>
>> b) the returned value is 3 words (e.g. a word is 8 bytes on a 64-bit
>> architecture like amd64) or less.
>> Both time.Time (3 words) and string (2 words) meet this criteria.
>>
>> Why this (b) heuristic? Because over 3 words and
>> I assume, as a heuristic -- that should be measured if it
>> matters -- that it will be faster to copy the one word pointer
>> and than the struct value. A pointer of course
>> is more likely to be faster to copy initially, but slower if
>> there is a cache miss on use or if it induces alot more garbage for
>> the garbage collector.
>>
>> A value is more likely to be on the stack, so typically less garbage;
>> and since its more likely to be on the stack, it is also more likely to
>> be in the
>> hardware's cache lines, so use will be faster. You can see why you
>> have to measure your actual use to see which is faster, if it
>> turns out the profiling shows that it is your bottleneck
>> and thus matters.
>>
>> And of course the other rules in the previously referenced
>> guidelines provided by Ian would also apply.
>>
>> So if you have a sync.Mutex value inside, or something
>> else that cannot be copied (sync.RWMutex, sync.WaitGroup),
>> then you _must_ return a pointer, per their documentation. Notice if you
>> had
>> a *sync.Mutex inside, then you might get away  with returning a value,
>> but
>> that gets tricky. Are shallow copies enforced, somehow? Will the user
>> make a mistake in copying them by value with a default shallow copy? Your
>> API design needs to balance the possibility of user error with space/time
>> efficiency.
>> The docs can say copies are forbidden (like sync or math/big below does),
>> but
>> the user might not read the docs, or remember their rules.
>>
>> More detail/an example:
>>
>> The idea of an immutable value can be subtle. An integer (as in the math
>> concept of an integer that can grow towards infinity and possibly become
>> very big)
>> is a good example of the tradeoffs.
>>
>> Usually an integer value fits in a word, because _usually_ they need only
>> need be under 64 bits (or 63 bits for signed), and can be represented
>> with
>> an int (word sized) or int64. For example: if you are incrementing an
>> 63-bit
>> positive integer once every clock cycle, and your clock cycle is
>> an optimistic 10GHz, so 0.1 nanosecond, and assuming that said word
>> integer can be incremented in a single clock cycle, then a 63 bit or 2^63
>> sized integer could be incremented for 29 years before overflowing,
>> since 2^63/(1e10 increments/sec*60 sec/minute*60 minutes/hour*
>> 24 hour/day*365.25 days/year) = 29.2271 years. Usually we assume
>> that our code is doing other things as well, and will be restarted and/or
>> ported to 128-bit or higher architectures before then.
>>
>> But notice the distinction when the integers need to get bigger today, say
>> for checking the math involved with cryptography: the math/big package
>> uses pointers for big integers because very big integers are going to
>> take up many
>> more words than 3. So the big package returns pointers and insists on
>> using
>> pointers. But that can mean user error if the user accidentally copies
>> them
>> by value. See the discussion at https://pkg.go.dev/math/big#Int
>>
>> > Operations always take pointer arguments (*Int) rather
>> > than Int values, and each unique Int value requires its own
>> > unique *Int pointer. To "copy" an Int value, an existing
>> > (or newly allocated) Int must be set to a new value using
>> > the Int.Set <https://pkg.go.dev/math/big#Int.Set> method; shallow
>> copies of Ints are not
>> > supported and may lead to errors.
>> >
>> > func (z *Int <https://pkg.go.dev/math/big#Int>) Set(x *Int
>> <https://pkg.go.dev/math/big#Int>) *Int <https://pkg.go.dev/math/big#Int> //
>> signature of math/big.Int.Set()
>>
>> Hope this helps capture some of the nuance. Generally pointers
>> are the safer bet, and values are a performance optimization.
>>
>> Jason
>>
>> On Thursday, October 31, 2024 at 7:47:25 PM UTC Tushar Rawat wrote:
>>
>>> Got it. I've read both the *references* shared, it seems these rules
>>> can be used to understand if we should pass the value or pointer to a 
>>> *function
>>> argument*.
>>> But, Can we really say that, the same rules apply for the return types
>>> as well ?
>>> I want to make idiomatic decision when to return a struct as value and
>>> when as pointer.
>>>
>>>
>>> Regards,
>>> Tushar
>>> On Thursday, October 31, 2024 at 11:03:13 PM UTC+5:30 Ian Lance Taylor
>>> wrote:
>>>
>>>> On Thu, Oct 31, 2024 at 6:24 AM Tushar Rawat <tusharr...@gmail.com>
>>>> wrote:
>>>> >
>>>> > I have seen few places where functions are returning struct as a
>>>> value (for instance time package, time is always returned as a value) and
>>>> however at some places folks prefer to return the pointers to struct
>>>> instead of the copy.
>>>> >
>>>> > Which one should be idiomatic approach, and when should we prefer
>>>> other once ?
>>>>
>>>> There are no hard and fast rules. There are some guidelines at
>>>> https://go.dev/wiki/CodeReviewComments#pass-values and
>>>> https://go.dev/wiki/CodeReviewComments#receiver-type.
>>>>
>>>> Ian
>>>>
>>> --
> 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+unsubscr...@googlegroups.com.
> To view this discussion visit
> https://groups.google.com/d/msgid/golang-nuts/371c3e96-c508-43db-8eca-2d83dfc41270n%40googlegroups.com
> <https://groups.google.com/d/msgid/golang-nuts/371c3e96-c508-43db-8eca-2d83dfc41270n%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>

-- 
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+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/golang-nuts/CA%2BKEDerTP_8w-Zstq%2BXr2xOB4rpX-Q__-FXXAhpqzFoNuf0rXg%40mail.gmail.com.

Reply via email to