Using new variable or updating existing variable in a loop

196 views
Skip to first unread message

Mohamad Rostami

unread,
Mar 19, 2024, 12:36:08 PM3/19/24
to golang-nuts
Hi,

I've seen in many places in go source code re-declaring a variable with the same name.
e.g:
for i < j {
   h := ...
}
Instead of
var h int
for i < j {
   h = ...
}

So I did a benchmark to check the differences. I didn't find any performance related differences, but in terms of Stack Memory in use, the second approach is better than the first one.

Not sure if the way is in standard library is by intention or something that should be ignored. 




Ian Lance Taylor

unread,
Mar 19, 2024, 12:46:36 PM3/19/24
to Mohamad Rostami, golang-nuts
The two versions are basically equivalent. How are you measuring
stack memory usage? If they are different, there may be something to
fix in the compiler.

Ian

Mohamad Rostami

unread,
Mar 19, 2024, 4:12:14 PM3/19/24
to golang-nuts
Well, I used runtime runtime.MemStats StackInuse.
I don't have much knowledge about compiler optimization. 
But to make it clear for myself:

considering these 2 functions:

//go:noinline
func A() {
   var h int
   for i := 0; i < 1e5; i++ {
     h = i
     _ = h
     fmt.Printf("%+v\n", &h)
   }
}

//go:noinline
func B() {
   for i := 0; i < 1e5; i++ {
     h := i
     _ = h
     fmt.Printf("%+v\n", &h)
   }
}

The address of h in B is changing in each iteration although it's not causing stack to grow.

If you point me to the documentation for this specific case, I would appreciate it.
Regards,

Kurtis Rader

unread,
Mar 19, 2024, 5:15:57 PM3/19/24
to Mohamad Rostami, golang-nuts
I would start by building it with inlining disabled and assembler output enabled. Then compare the assembly code for main.A and main.B.

go build -gcflags 'all=-l -S' x.go 2>y

--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/3818a025-d46c-4e23-8b2e-6e0a08c0986an%40googlegroups.com.


--
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank

Kurtis Rader

unread,
Mar 19, 2024, 6:20:03 PM3/19/24
to Mohamad Rostami, golang-nuts
Also, if you tell the compiler to report memory allocations you'll see something like this:

./x.go:10:13: inlining call to fmt.Printf
./x.go:18:13: inlining call to fmt.Printf
./x.go:7:6: moved to heap: h
./x.go:10:13: ... argument does not escape
./x.go:17:3: moved to heap: h
./x.go:18:13: ... argument does not escape

The issue isn't that the stack is growing. The issue is the `h := h` assignment in B() is causing a new heap allocation each time through the loop. I don't know why either var definition is escaping to the heap but the increasing address for the B() case is because you're making 1e5 instances of that heap var.

On Tue, Mar 19, 2024 at 1:12 PM Mohamad Rostami <mb.ros...@gmail.com> wrote:
--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/3818a025-d46c-4e23-8b2e-6e0a08c0986an%40googlegroups.com.

Mohamad Rostami

unread,
Mar 20, 2024, 4:48:03 AM3/20/24
to golang-nuts
Thanks @kurtis,
I actually have both functions without fmt print function, so no allocation is happening.
I also did compare assembly codes with your command, and can confirm both are equal.
go 1.21.0 darwin/arm64
Reply all
Reply to author
Forward
0 new messages