8

When I searched about the '$' prefix in Julia, all I could find is that it is for string or expression interpolation. For example, here https://docs.julialang.org/en/v1/base/punctuation/. However, I have seen people' code like

add_broadcast!($y_d, $x_d)

as in this tutorial https://cuda.juliagpu.org/stable/tutorials/introduction/. Here the "$" sign cannot be interpolation, can it? There is nothing about such usage in the functions doc either https://docs.julialang.org/en/v1/manual/functions/. So I am very confused. Any idea is appreciated. Thanks!

user2727768
  • 666
  • 1
  • 7
  • 12

1 Answers1

11

The $ sign expressions like you have shown is non-standard Julia code and it typically appears only in expressions passed to macros. This is exactly the case in your example, where the full line is:

@btime add_broadcast!($y_d, $x_d)

which uses @btime macro from BenchmarkTools.jl. And if you go to Quick Start section there you can read:

If the expression you want to benchmark depends on external variables, you should use $ to "interpolate" them into the benchmark expression to avoid the problems of benchmarking with globals. Essentially, any interpolated variable $x or expression $(...) is "pre-computed" before benchmarking begins:

So in short with @btime you use $ to "interpolate" them into the benchmarked expression in order to get a correct benchmark results.

The $ sign is used with macros to interpolate also in other packages, e.g. DataFrameMacros.jl.


EDIT:

An example how not using $ affects execution time when referencing to non-const global variable:

julia> using BenchmarkTools

julia> x = 1
1

julia> @btime (y = 0; for _ in 1:10^6 y += x end; y) # slow and a lot of allocations
  22.102 ms (999489 allocations: 15.25 MiB)
1000000

julia> @btime (y = 0; for _ in 1:10^6 y += $x end; y) # loop is optimized out
  5.600 ns (0 allocations: 0 bytes)
1000000

julia> const z = 1
1

julia> @btime (y = 0; for _ in 1:10^6 y += z end; y) # loop is optimized out
  5.000 ns (0 allocations: 0 bytes)

You can think of it as follows. In the above example not using $ is as-if you have created and run the following function:

function temp1()
    y = 0
    for _ in 1:10^6
        y += x
    end
    y
end

And you get:

julia> @btime temp1()
  22.106 ms (999489 allocations: 15.25 MiB)
1000000

While using $ is as if defined x inside the body of the function like this:

function temp2()
    x = 1
    y = 0
    for _ in 1:10^6
        y += x
    end
    y
end

and now you have:

julia> @btime temp2()
  5.000 ns (0 allocations: 0 bytes)
1000000
Bogumił Kamiński
  • 66,844
  • 3
  • 80
  • 107
  • 1
    It's also used in `@everywhere` in the Distributed stdlib – Frames Catherine White Jul 05 '21 at 21:36
  • 2
    Right, I do not know why, but usually DataFrames.jl related examples spring to my mind first :). – Bogumił Kamiński Jul 05 '21 at 21:48
  • 1
    Thanks for the quick answer. Basically, it is used to "evaluate" the arguments for those micros to work as expected, no? I can see why this is helpful for expressions passed as arguments. If it is just a variable that binds to a string or int, does it mean there is no difference with or without the $? – user2727768 Jul 05 '21 at 22:05
  • It matters, as variable bound to a constant in a global scope without const might lead to type unstable code. I have updated my answer with the example. – Bogumił Kamiński Jul 06 '21 at 06:53
  • Thanks. That makes a lot of sense. Your answer accepted. Just to follow up if you don't mind. I guess what you said mostly applies to the micros due to how they work. If I call your function f(x) normally, there is no advantage in using f($x), right? – user2727768 Jul 06 '21 at 16:12
  • If you call the function normally then `f($x)` is an invalid syntax - you must write `f(x)`. That is why I have said that `$` is special. It is used in macros and string interpolation only. – Bogumił Kamiński Jul 06 '21 at 22:06