[Inefficiency of] using a calculated value in for loop condition

I was reading an example of some Go code recently and ran into something which seems inconsistent with what I’d learned as a computer science “best practice”. I see things like this quite a bit but this was from a rather authoritative source, so it stuck out.

Honestly there is nothing urgent or very interesting about this question… a light-weight CS thought experiment perhaps is all. But it’s come to mind a couple of times recently and it’s a question that I don’t have an answer for. So here goes.

When I was learning C, I was taught that using a len() type of function in a for loop condition was inefficient because the function would be called each time the condition is checked, as in this trivial Go echo-like function:

func main() {
    var s string
    for i := 1; i < len(os.Args); i++ {
        s += os.Args[i] + " "
    }
    fmt.Println(s)
}

I was taught that it is more efficient to declare a variable and assign it the value of len() and then use that variable in the for loop condition. This way the function call happens once and not each time the code block loops.

func main() {
    var s string
    l := len(os.Args)
    for i := 1; i < l ; i++ {
        s += os.Args[i] + " "
    }
    fmt.Println(s)
}

One unfortunate thing about this second version is that it introduces a new variable that out-lives the for loop and which may be of no use later in the program (I had also been taught to keep scope tight). There other ways to narrow that new variable’s scope, but that means adding a little more code, obscuring things just that little bit more.

The first example seems more elegant and readable… but is it inherently inefficient?

In something like echoing command line args there aren’t enough iterations through the loop to really make any difference at all, but what if the loop was going to run millions or billions of times throughout the course of your program?

I’m am not a premature (nor micro) optimizer but I do hold needless waste in disfavor…

So now I’ll let my mind wander back onto things that I should be solving and let someone else take up this thread if they want to… Maybe I’ll run some benchmarks after I’m done working.

3 Likes

Yes, I was also taught the second way is more efficient and it would be how I do it as well. Also the second version allows you to reuse the length again by using the variable “l” without having to recalculate the length.

Stick to the second version.

Len(struct) is not a function, it is a built in keyword that gives you access to the slice header’s length field.

Go’s len built in is not the same as strlen and friend from other languages. It is a constant time field access.

5 Likes

Btw, use range

for _, arg := range os.Args[1:] { ... }
2 Likes

Or

for i, n := 1, len(os.Args); i < n ; i++ { ... }
7 Likes

Len(struct) is not a function, it is a built in keyword that gives you access to the slice header’s length field.

Go’s len built in is not the same as strlen and friend from other languages. It is a constant time field access.

Thanks for the answers, each of them. Can you point me to where I can find more about this in the docs?

https://golang.org/ref/spec#Length_and_capacity

1 Like

http://blog.golang.org/go-slices-usage-and-internals

1 Like

@dfc, about range, which one is more efficient in Go,

Case 1:

for i,_ := range SomeSlice {
   // work with slice element through SomeSlice[i]
}

Case 2:

for i, el := range SomeSlice {
    // work with slice element `el`
}

Logically, case 1 require less stack used because of less variable copied in each range iteration. Am I right?

Does slice indexing work like C? Pointer to address? I assume the slice indexing work like this:

func slice[i] sliceType {
    return slice.ptr[i]
}

If that is correct, can I assume case 1 is more efficient than case 2 both in time and memory?

Thank you.

Write a benchmark and find out.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.