# Why isn't 10^6 evaluated at compile time?

**URL:** <https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124>\
**Category:** Performance\
**Created:** [May 25, 2020, 1:17pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124 "2020-05-25T13:17:47Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![robsmith11](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/robsmith11/32/29641_2.png) [@robsmith11](https://discourse.julialang.org/u/robsmith11)\
**Post date:** [May 25, 2020, 1:17pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/1 "2020-05-25T13:17:47Z")

</div>

I was optimizing some code in the hot path and was surprised to see that constants written as `10^6` were being evaluated at run-time. Isn’t evaluating constants like this one of the simplest optimizations? Why wouldn’t Julia simplify it?

```julia
julia> function f(x)
        x*10^6
       end
f (generic function with 1 method)

julia> @code_native f(1)
        .text
; ┌ @ REPL[27]:1 within `f'
        pushq %rbx
        movq %rdi, %rbx
; │ @ REPL[27]:2 within `f'
; │┌ @ none within `literal_pow'
; ││┌ @ none within `macro expansion'
; │││┌ @ intfuncs.jl:273 within `^'
        movabsq $power_by_squaring, %rax
        movl $10, %edi
        movl $6, %esi
        callq *%rax
; │└└└
; │┌ @ int.jl:87 within `*'
        imulq %rbx, %rax
; │└
        popq %rbx
        retq
; └

```

---

<div class="post-metadata">

**Author:** ![lobingera](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lobingera/32/211_2.png) [@lobingera](https://discourse.julialang.org/u/lobingera)\
**Post date:** [May 25, 2020, 4:15pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/2 "2020-05-25T16:15:48Z")

</div>

Because 1e6 exists.

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [May 25, 2020, 4:18pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/3 "2020-05-25T16:18:31Z")

</div>

Sorry, I’m not familiar with this. If one uses `1e6` instead of `10^6` is that number then pre-compiled and if so does that speed up the running of the code?

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [May 25, 2020, 4:24pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/4 "2020-05-25T16:24:25Z")

</div>

> [@lobingera](#):
>
> Because 1e6 exists.

This isn’t the answer, and it would lead to a function with different behavior, it would always produce floats with at least 64 bit precision.

I’m afraid I don’t know the answer, @Ian_Slagle, but you could use `1_000_000` instead (the underscores are optional).

BTW I may not actually make a difference at runtime, appearances notwithstanding. You should benchmark with BenchmarkTools.jl to tell for sure.

---

<div class="post-metadata">

**Author:** ![tamasgal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamasgal/32/27946_2.png) [@tamasgal](https://discourse.julialang.org/u/tamasgal)\
**Post date:** [May 25, 2020, 4:36pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/5 "2020-05-25T16:36:22Z")

</div>

I think the assembler code is evidence enough, but here you go:

```julia
julia> using BenchmarkTools

julia> function f1(x)
           x*10^6
       end
f1 (generic function with 1 method)

julia> function f2(x)
           x*1e6
       end
f2 (generic function with 1 method)

julia> @btime f1(x) setup=(x=rand()) evals=1;
  32.000 ns (0 allocations: 0 bytes)

julia> @btime f2(x) setup=(x=rand()) evals=1;
  29.000 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [May 25, 2020, 4:37pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/6 "2020-05-25T16:37:19Z")

</div>

`1e6` is a float, you want to test `x*10^6` against `x*1000000;`

---

<div class="post-metadata">

**Author:** ![tamasgal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamasgal/32/27946_2.png) [@tamasgal](https://discourse.julialang.org/u/tamasgal)\
**Post date:** [May 25, 2020, 4:37pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/7 "2020-05-25T16:37:33Z")

</div>

You’re right…

No difference though:

```julia
julia> function f2(x)
           x*1_000_000
       end
f2 (generic function with 1 method)

julia> @btime f2(x) setup=(x=rand()) evals=1;
  29.000 ns (0 allocations: 0 bytes)

```

It seems like there is no purity guarantee for that method.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [May 25, 2020, 4:40pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/8 "2020-05-25T16:40:07Z")

</div>

hmmm, you’re calling it with the output of rand() which is a float, so it’s all going to be converted to float anyway. So if you’re using floats anyway 1e6 is your best bet.

---

<div class="post-metadata">

**Author:** ![tamasgal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamasgal/32/27946_2.png) [@tamasgal](https://discourse.julialang.org/u/tamasgal)\
**Post date:** [May 25, 2020, 4:41pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/9 "2020-05-25T16:41:12Z")

</div>

I didn’t tell the full story, it’s (almost) the same when calling with `Int64` 😉 I just assumed the usual call of the original function is a float…

```julia
julia> @btime f1(x) setup=(x=rand(Int64)) evals=1;
  32.000 ns (0 allocations: 0 bytes)

julia> @btime f2(x) setup=(x=rand(Int64)) evals=1;
  28.000 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [May 25, 2020, 4:44pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/10 "2020-05-25T16:44:21Z")

</div>

So, it basically saves you ~ 10% of the function eval to use a fixed constant. **when the function is a trivial multiply function**. But of course most people write functions where much more occurs than multiplying by a constant. When the function has say 11 steps, the calculation of a constant at the beginning will involve maybe 1% instead of 10%.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [May 25, 2020, 4:49pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/11 "2020-05-25T16:49:53Z")

</div>

This doesn’t make a lot of sense to me. Raising an integer to the 6th power takes ~10% as long as a single multiplication? Really?

---

<div class="post-metadata">

**Author:** ![tamasgal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamasgal/32/27946_2.png) [@tamasgal](https://discourse.julialang.org/u/tamasgal)\
**Post date:** [May 25, 2020, 4:50pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/12 "2020-05-25T16:50:40Z")

</div>

I am also wondering but I think the way I benchmarked it is ok. Any ideas?

There is definitely something “more” happening in the first function, not sure what 😉

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [May 25, 2020, 4:52pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/13 "2020-05-25T16:52:29Z")

</div>

> [@dlakelan](#):
>
> 1% instead of 10%.

Okay so what is the upside for loosing that 1%? I mean what does the compiler gain in not converting it to a constant? Granted that assumes this was done for a reason. Maybe the compiler developers feel that we should multiple our own \*\*\* \*\*\*\*\* constants? 🙂

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [May 25, 2020, 4:52pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/14 "2020-05-25T16:52:45Z")

</div>

I don’t have an explanation. I just wanted to point out that reading the assembly can be a bit confusing sometimes, so benchmarking is a good idea, no matter how it looks.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [May 25, 2020, 4:54pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/15 "2020-05-25T16:54:53Z")

</div>

It’s a good question about why it’s not statically precalculated. It seems likely this is just a kind of optimization that doesn’t yet exist or the compiler didn’t catch it in this case.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [May 25, 2020, 4:58pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/16 "2020-05-25T16:58:38Z")

</div>

I’ve seen this before. There’s simply a limit to how far this literal is calculated:

```julia
julia> foo(x) = x * 10^5;

julia> bar(x) = x * 10^4;

julia> @code_native foo(1)
	.section	__TEXT,__ text,regular,pure_instructions
; ┌ @ REPL[54]:1 within `foo'
	movl	$10, %eax
	movl	$2, %ecx
	nopw	(%rax,%rax)
; │┌ @ none within `literal_pow'
; ││┌ @ none within `macro expansion'
; │││┌ @ intfuncs.jl:238 within `^'
; ││││┌ @ intfuncs.jl:226 within `power_by_squaring'
; │││││┌ @ int.jl:54 within `*'
L16:
	imulq	%rax, %rax
; │││││└
; │││││ @ intfuncs.jl:225 within `power_by_squaring'
; │││││┌ @ operators.jl:341 within `>='
; ││││││┌ @ int.jl:410 within `<='
	addq	$-1, %rcx
; │││││└└
	jg	L16
	imulq	%rdi, %rax
; │└└└└
; │┌ @ int.jl:54 within `*'
	addq	%rax, %rax
	leaq	(%rax,%rax,4), %rax
; │└
	retq
	nopw	%cs:(%rax,%rax)
; └

julia> @code_native bar(1)
	.section	__TEXT,__ text,regular,pure_instructions
; ┌ @ REPL[55]:1 within `bar'
; │┌ @ REPL[55]:1 within `*'
	imulq	$10000, %rdi, %rax ## imm = 0x2710
; │└
	retq
	nopl	(%rax,%rax)
; └

```

So it works for `10^4` but not `10^5` and up.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [May 25, 2020, 5:06pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/17 "2020-05-25T17:06:02Z")

</div>

Also, I’m not sure about the benchmarking here. I tried the `Ref` trick instead of `evals=1`, since that will probably be limited by some minimum timing quantization.

```julia
julia> x = 3
3

julia> @btime foo($(Ref(x))[]);
  3.180 ns (0 allocations: 0 bytes)

julia> @btime bar($(Ref(x))[]);
  1.456 ns (0 allocations: 0 bytes)

```

This seems more reasonable for a simple multiplication.

So, the answer, I think, is that, yes, there is a difference in performance, and it’s due to a limitation in how far constant propagation goes for certain operations like `power_by_squaring`.

And you can solve it by using a different literal:

```julia
julia> baz(x) = x * 1_000_000;

julia> @btime baz($(Ref(x))[])
  1.456 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![tamasgal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamasgal/32/27946_2.png) [@tamasgal](https://discourse.julialang.org/u/tamasgal)\
**Post date:** [May 25, 2020, 5:09pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/18 "2020-05-25T17:09:22Z")

</div>

Yep, good catch. I switched to the `eveals=1` since it has less noise but now I revise 😉

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [May 25, 2020, 5:13pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/19 "2020-05-25T17:13:29Z")

</div>

Here’s some more fun:

```julia
julia> foo(x) = x * 10^8;

julia> @code_native foo(1)
	.section	__TEXT,__ text,regular,pure_instructions
; ┌ @ REPL[95]:1 within `foo'
; │┌ @ REPL[95]:1 within `*'
	imulq	$100000000, %rdi, %rax ## imm = 0x5F5E100
; │└
	retq
	nopl	(%rax,%rax)
; └

julia> foo(x) = x * 10^16;

julia> @code_native foo(1)
	.section	__TEXT,__ text,regular,pure_instructions
; ┌ @ REPL[97]:1 within `foo'
	movabsq	$10000000000000000, %rax ## imm = 0x2386F26FC10000
; │┌ @ int.jl:54 within `*'
	imulq	%rdi, %rax
; │└
	retq
	nop
; └

julia> foo(x) = x * 10^32;

julia> @code_native foo(1) # ooooops. overflow
	.section	__TEXT,__ text,regular,pure_instructions
; ┌ @ REPL[99]:1 within `foo'
	movabsq	$-8814407033341083648, %rax ## imm = 0x85ACEF8100000000
; │┌ @ int.jl:54 within `*'
	imulq	%rdi, %rax
; │└
	retq
	nop
; └

```

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [May 25, 2020, 6:05pm UTC](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124/20 "2020-05-25T18:05:26Z")

</div>

Yeah, this is sometimes a little annoying, but one option you always have is to write a macro that just `eval`s an expression inside the macro body and then **never use it on expressions involving local variables and/or impure operations** :

```julia
macro eval_at_parse_time(ex)
    eval(ex)
end

let x = Ref(4)
    f1(x) = x * 10^6
    f2(x) = x * 1_000_000
    f3(x) = x * @eval_at_parse_time 10^6

    @btime f1($x[])
    @btime f2($x[])
    @btime f3($x[])
end

```

```julia
3.359 ns (0 allocations: 0 bytes)
1.299 ns (0 allocations: 0 bytes)
1.299 ns (0 allocations: 0 bytes)

```

[Next page](https://discourse.julialang.org/t/why-isnt-10-6-evaluated-at-compile-time/40124.md?page=2)
