# For loop with variable range so slow

**URL:** <https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149>\
**Category:** General Usage\
**Created:** [March 11, 2026, 12:16pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149 "2026-03-11T12:16:12Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![kila\_suelika](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kila_suelika/32/221151_2.png) [@kila\_suelika](https://discourse.julialang.org/u/kila_suelika)\
**Post date:** [March 11, 2026, 12:16pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/1 "2026-03-11T12:16:12Z")

</div>

Recent I find my julia program cost 3.7s for each iteration while c++ only use 0.022s. By test I find for loop with variable range seems make program slow.

```julia-auto
using SQLite, DataFrames, Statistics, Serialization, Dates, Printf

n_T = 100
for i = 1:2
    @time begin
        n = 1
        for j = 1:100
            n += j
        end
    end
end

```

Result:

```julia-auto
  0.000000 seconds
  0.000000 seconds

```

while for:

```julia-auto
using SQLite, DataFrames, Statistics, Serialization, Dates, Printf

n_T = 100
for i = 1:2
    @time begin
        n = 1
        for j = 1:n_T
            n += j
        end
    end
end

```

result:

```julia-auto
  0.000059 seconds (180 allocations: 4.688 KiB)
  0.000012 seconds (170 allocations: 4.234 KiB)

```

the compilier seems not smart enough to elliminate such a simple loop.

---

<div class="post-metadata">

**Author:** ![ForceBru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/forcebru/32/21389_2.png) [@ForceBru](https://discourse.julialang.org/u/ForceBru)\
**Post date:** [March 11, 2026, 12:31pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/2 "2026-03-11T12:31:23Z")

</div>

Classic case of non-constant global variables being slow.

Put `n_T` in a local scope — and it’s fast again:

```Julia
julia> let
       n_T = 100
       for i = 1:2
           @time begin
               n = 1
               for j = 1:n_T
                   n += j
               end
           end
       end
       end
  0.000000 seconds
  0.000000 seconds

```

Or make `n_T` constant:

```Julia
julia> const n_T = 100;

julia> for i = 1:2
           @time begin
               n = 1
               for j = 1:n_T
                   n += j
               end
           end
       end
  0.000000 seconds
  0.000000 seconds

```

In general, accessing global variables may require allocations, making the code slower.

---

<div class="post-metadata">

**Author:** ![kila\_suelika](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kila_suelika/32/221151_2.png) [@kila\_suelika](https://discourse.julialang.org/u/kila_suelika)\
**Post date:** [March 11, 2026, 12:48pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/4 "2026-03-11T12:48:05Z")

</div>

Why compilier didn’t optimize it? I thought this is a very basic optimization in C/C++.

---

<div class="post-metadata">

**Author:** ![karei](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/karei/32/214809_2.png) [@karei](https://discourse.julialang.org/u/karei)\
**Post date:** [March 11, 2026, 1:05pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/5 "2026-03-11T13:05:27Z")

</div>

Good question.

In C/C++, every program starts from `main()`. To make the comparison fair, you should also put your Julia code inside a function. Then you will get the expected performance.

```julia
function main()
    n_T = 100
    for i = 1:2
        @time begin
            n = 1
            for j = 1:n_T
                n += j
            end
        end
    end
end

main()

```

So there is more than one way to fix this:

1. As ForceBru mentioned, use `let` to introduce a local scope.
2. As ForceBru also mentioned, mark the global variable as `const`. This is a recommended approach.[1]
3. Put performance-critical code inside a function. This is also a recommended approach.[2]

[1] [https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-untyped-global-variables](https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-untyped-global-variables)  
[2] [https://docs.julialang.org/en/v1/manual/performance-tips/#Performance-critical-code-should-be-inside-a-function](https://docs.julialang.org/en/v1/manual/performance-tips/#Performance-critical-code-should-be-inside-a-function)

---

<div class="post-metadata">

**Author:** ![ForceBru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/forcebru/32/21389_2.png) [@ForceBru](https://discourse.julialang.org/u/ForceBru)\
**Post date:** [March 11, 2026, 1:06pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/6 "2026-03-11T13:06:58Z")

</div>

Yet another option is to make the global variable typed:

```Julia
julia> n_T::Int = 100 # assign type!
100

julia> for i = 1:2
                  @time begin
                      n = 1
                      for j = 1:n_T
                          n += j
                      end
                  end
              end
  0.000000 seconds
  0.000000 seconds

```

You can subsequently assign another integer to `n_T` and it’ll remain fast.

As discussed here ([Why is there a performance difference between global and local variables in non-interactive Julia programs?](https://discourse.julialang.org/t/why-is-there-a-performance-difference-between-global-and-local-variables-in-non-interactive-julia-programs/5753)):

> A global variable might have its value, and therefore its type, change at any point. This makes it difficult for the compiler to optimize code using global variables. Variables should be local, or passed as arguments to functions, whenever possible.

However, if you fix the type (like `n_T::Int = 100`), this is no longer a problem. This was introduced in Julia 1.8 ([Types · The Julia Language](https://docs.julialang.org/en/v1/manual/types/#Type-Declarations)):

> As of Julia 1.8, type declarations can now be used in global scope i.e. type annotations can be added to global variables to make accessing them type stable.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [March 11, 2026, 1:10pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/7 "2026-03-11T13:10:40Z")

</div>

In general, things that must run fast should be put inside functions. The unit of compilation is function. Things that are written directly at the julia prompt may not be compiled properly, and certainly not if it uses non-`const` globals.

```julia
function sumit(n_t)
    n = 1
    for j = 1:n_t
        n += j
    end
    return n
end
julia> @time sumit(100)
  0.000000 seconds
5051

```

Indeed, the loop has been optimized away, just a multiplication and a shift is left, i.e. just  
1 + n\_t(n\_t+1)/2:

```julia-auto
julia> @code_native sumit(100)
...

```

---

<div class="post-metadata">

**Author:** ![Tyrone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tyrone/32/221141_2.png) [@Tyrone](https://discourse.julialang.org/u/Tyrone)\
**Post date:** [March 11, 2026, 2:12pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/8 "2026-03-11T14:12:59Z")

</div>

The Compiler indeed failed to optimize the code due to that the global variable was not typed.

Full technical details:

I included the suggestion of @karei and looked into the runtime behavior, this is what I noticed:

Full Example code:

```julia-auto

function one()
	for i = 1:2
		n = 1
		for j = 1:100
			n += j
		end
	end
end

function two()
	n_T_local = 100
	for i = 1:2
		n = 1
		for j = 1:n_T_local
			n += j
		end
	end
end

n_T = 100
function three()
	for i = 1:2
		n = 1
		for j = 1:n_T
			n += j
		end
	end
end

@cgprofile one()
@cgprofile two()
@cgprofile three()

```

It reported that function `one()` and `two()` was fully optimized and inlined by the compiler, no allocations or other calls to other methods.

however function `three()` reported the following:

```julia-auto
# Reconstructed runtime executed code, showing methods and allocations:
function three()
	for i = 1:2
		n = 1
		# Allocation of 2 UnitRange{Int64} for 32 bytes each iteration of i, total 64 bytes.
		n_T_iter::UnitRange{Int64} = n_T
		j_next::Union{Nothing, Tuple{Int64, Int64}} = (dynamic dispatch call) Main.Base.iterate(n_T_iter::UnitRange{Int64}) in range.jl:917
		while j_next !== nothing # Iterated 100 times each iteration of i, 200 iterations in total.
			# Allocation of 200 Tuple{Int64,Int64} for 32 bytes each iteration of j_next, total 6400 bytes
    		(item, state)::Tuple{Int64,Int64} = j_next
			n = (dynamic dispatch call) Main.base.+(n::Int64, item::Int64)::Int64 in int.jl:87
			# Allocation of 138 Int64 for 16 bytes each iteration after iteration 31 of j_next, total of 2208 bytes
			?::Int64;
			j_next = (dynamic dispatch call) Main.Base.iterate(n_T_iter::UnitRange{Int64}, item::Int64)::Union{Nothing, Tuple{Int64, Int64}} in range.jl:919
		end
	end
end

```

This shows that it was unable to determine n\_T at compile time, thus having to insert dynamic dispatch calls that needed to be resolved during runtime.

I also ran @ForceBru & @sgaure suggestions

```julia-auto

n_T_typed::Int = 100
function four()
	for i = 1:2
		n = 1
		for j = 1:n_T_typed
			n += j
		end
	end
end

function sumit(n_t)
    n = 1
    for j = 1:n_t
        n += j
    end
    return n
end

@cgprofile four()
@cgprofile sumit(100)

```

function `four()` and `sumit()` was also fully optimized and inlined by the compiler, no allocations no other calls to other methods.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [March 11, 2026, 3:04pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/9 "2026-03-11T15:04:08Z")

</div>

Just to explain what happens when an untyped global is used (a `const` is always typed). The loop `for j in 1:n_t` contains the range `1:n_t`. If the compiler is not sure about the type of `n_t` (which it can’t be with an untyped global `n_t`), the `1:n_t` will create a `UnitRange{Any}(1, n_t)`, i.e. a range which iterates over values which the compiler knows nothing about (`Any`). Or just a lookup call for `:`.

The `n += j` is rewritten as `n = n + j`, but the compiler knows nothing about the type of `j`. It has to generate code for looking up which `+` to use for every iteration in the loop. Since the compiler doesn’t know which `+` to use, it doesn’t know the type of the result either, so it knows nothing about `n` and nothing about `j` inside the loop. Such lookup takes a lot of time.

On the other hand, if the compiler knows the type of `n_t` as an `Int`, it will create the range `UnitRange{Int}(1, n_t)`, which iterates over `Int`s. It knows `n` starts out as an `Int`, the `n + j` is compiled to a single instruction, and it knows the result is an `Int`, so `n` will continue to be an `Int`. Moreover, the compiler is then actually able to recognize this sum, and replace it with `1 + n_t*(n_t+1)>>1` or similar, optimizing the entire loop away.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [March 11, 2026, 3:50pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/10 "2026-03-11T15:50:37Z")

</div>

> [@Tyrone](#):
>
> Small note on sumit(n\_t), the compiler did assume Int64 instead of Int:

If you are using a 64-bit Julia, those are the same thing.

```julia-auto
julia> Int64 === Int
true

```

---

<div class="post-metadata">

**Author:** ![Tyrone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tyrone/32/221141_2.png) [@Tyrone](https://discourse.julialang.org/u/Tyrone)\
**Post date:** [March 11, 2026, 3:51pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/11 "2026-03-11T15:51:26Z")

</div>

Oh haha thanks, somehow never noticed that before 🙂

---

<div class="post-metadata">

**Author:** ![technocrat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/technocrat/32/220947_2.png) [@technocrat](https://discourse.julialang.org/u/technocrat)\
**Post date:** [March 11, 2026, 6:52pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/12 "2026-03-11T18:52:34Z")

</div>

`x` should be large enough to show difference

```julia-auto
using BenchmarkTools

function loop_sum(x::Int)
    n = 1
    for j = 1:x
        n += j
    end
    return n
end

function comp_sum(x::Int)
    return 1 + sum(j for j = 1:x)
end

@btime loop_sum(1000000)
@btime comp_sum(1000000)

```

1.666 ns (0 allocations: 0 bytes)  
0.916 ns (0 allocations: 0 bytes)```

```julia-auto
Comprehensions are to be preferred over for loops.
```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 11, 2026, 8:06pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/13 "2026-03-11T20:06:55Z")

</div>

> [@technocrat](#):
>
> `Comprehensions are to be preferred over for loops.`

Nope.

1. You don’t have a comprehension, you have a generator. (A comprehension allocates an array, whereas a generator is [“lazy”](https://en.wikipedia.org/wiki/Lazy_evaluation).)
2. `sum` is implemented by loops written in Julia too … `for` loops, written properly, are not slow!
3. Your benchmark is broken because **the compiler is optimizing away the whole loop** , so you are basically measuring noise.

In particular, you’ll notice that `@btime loop_sum(10)` reports exactly the same time as `@btime loop_sum(10^6)`, which should give you a clue: the compiler is smart enough to realize that this sum is an [arithmetic series](https://mathworld.wolfram.com/ArithmeticSeries.html) and just replaces the whole loop with the arithmetic-series formula.

A more interesting benchmark, which at least doesn’t get optimized away to nothing, is:

```julia-auto
function loop_sum2(n)
    s = 0.0
    for j = 1:n
        s += 1/j^2
    end
    return s
end

comp_sum2(n) = sum(1/j^2 for j = 1:n)

```

which gives (on my [M1 max](https://en.wikipedia.org/wiki/Apple_M1)):

```julia-auto
julia> @btime loop_sum2(10000)
  9.750 μs (0 allocations: 0 bytes)
1.6448340718480652

julia> @btime comp_sum2(10000)
  9.750 μs (0 allocations: 0 bytes)
1.6448340718480652

```

However, you can make `loop_sum2` faster by changing `for` to [`@simd for`](https://docs.julialang.org/en/v1/base/base/#Base.SimdLoop.@simd) (which gives the compiler permission to change the answer slightly by re-ordering the sum to use [SIMD](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data)), and this gives:

```julia-auto
julia> @btime loop_sum2(10000)
  3.865 μs (0 allocations: 0 bytes)
1.6448340718480614

```

If you use the functional form of `sum`, it is able to insert `@simd` for you (because this form, like `reduce`, is documented to allow re-ordering of the summands), and gets almost the same speed, with a slight overhead because it uses [pairwise summation](https://en.wikipedia.org/wiki/Pairwise_summation) to get a more accurate sum:

```julia-auto
func_sum2(n) = sum(j -> 1/j^2, 1:n)

```

gives

```julia-auto
julia> @btime func_sum2(10000)
  3.979 μs (0 allocations: 0 bytes)
1.6448340718480614

```

---

<div class="post-metadata">

**Author:** ![technocrat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/technocrat/32/220947_2.png) [@technocrat](https://discourse.julialang.org/u/technocrat)\
**Post date:** [March 11, 2026, 10:34pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/14 "2026-03-11T22:34:58Z")

</div>

Thanks for bringing me up short. Now that I’m better @btime aware, it will be a help

```julia-auto
using BenchmarkTools

function loop_sum(x::Int)
    n = 1
    for j = 1:x
        n += j
    end
    return n
end

function comp_sum(x::Int)
    return 1 + sum(j for j = 1:x)
end

println("loop_sum")
@btime loop_sum($1_000_000)
println("comp_sum")
@btime comp_sum($1_000_000)
function loop_sum2(n::Float64)

    s = 0.0
    for j = 1:n
        s += 1 / j^2
    end
    return s
end
comp_sum2(n::Float64) = sum(1 / j^2 for j = 1:n)

println("comp_sum2")
@btime comp_sum2($1e6)
println("loop_sum2")
@btime loop_sum2($1e6)

```

```julia-auto

loop_sum
  1.666 ns (0 allocations: 0 bytes)
comp_sum
  1.708 ns (0 allocations: 0 bytes)
comp_sum2
  1.089 ms (0 allocations: 0 bytes)
loop_sum2
  1.089 ms (0 allocations: 0 bytes)
1.6449330
6684877

```

And generators! I had totally forgotten about that pattern.

---

<div class="post-metadata">

**Author:** ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)\
**Post date:** [March 11, 2026, 11:42pm UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/15 "2026-03-11T23:42:13Z")

</div>

Did you mistype ms as ns?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 12, 2026, 12:00am UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/16 "2026-03-12T00:00:32Z")

</div>

> [@technocrat](#):
>
> ```julia-auto
> loop_sum
> 1.666 ns (0 allocations: 0 bytes)
> 
> ```

It’s still optimizing away the loop, so this benchmark is meaningless.

Using `$` interpolation in `@btime` is only useful for non-constant globals. It does nothing for numeric literals. And it has no effect on the compiler’s ability to replace the loop with an arithmetic-series formula.

Note also that declaring the argument types has no effect on performance here. You might as well leave them out (as in my post). This is a common point of confusion — see [“argument-type declarations” in the manual](https://docs.julialang.org/en/v1/manual/functions/#Argument-type-declarations).

And using a floating point `n` is allowed but not typical for loops over `1:n`.

---

<div class="post-metadata">

**Author:** ![kila\_suelika](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kila_suelika/32/221151_2.png) [@kila\_suelika](https://discourse.julialang.org/u/kila_suelika)\
**Post date:** [March 12, 2026, 6:31am UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/17 "2026-03-12T06:31:49Z")

</div>

I tried to slightly modify my original code and find it cost 0.028/each iter. Indeed parallel with C++. Impresive. Now I’m going to use Julia as my main daily programming language.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [March 12, 2026, 8:19am UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/18 "2026-03-12T08:19:21Z")

</div>

Since it hasn’t been mentioned, the [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/) is a good place to look when performance is lower than expected.

---

<div class="post-metadata">

**Author:** ![kila\_suelika](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kila_suelika/32/221151_2.png) [@kila\_suelika](https://discourse.julialang.org/u/kila_suelika)\
**Post date:** [March 13, 2026, 12:49am UTC](https://discourse.julialang.org/t/for-loop-with-variable-range-so-slow/136149/19 "2026-03-13T00:49:54Z")

</div>

Thank you, it’s useful. I find my problem is just in the first section of the doc.
