# Macro to choose macros

**URL:** https://discourse.julialang.org/t/macro-to-choose-macros/96507
**Category:** General Usage
**Tags:** macros
**Created:** [March 23, 2023, 1:08pm UTC](https://discourse.julialang.org/t/macro-to-choose-macros/96507 "2023-03-23T13:08:34Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)
#### Post date: [March 23, 2023, 1:08pm UTC](https://discourse.julialang.org/t/macro-to-choose-macros/96507/1 "2023-03-23T13:08:34Z")

</div>

It seems I still don’t understand how macros really work in Julia. (sigh)

I want to define a macro `@always_fast` which will insert `@simd` or `Threads.@threads` depending on the value of `Threads.nthreads()` for cases like:

```julia
@always_fast for i in eachindex(x,y)
   x[i] = f(y[i])
end

```

or

```julia
s = 0
@always_fast for y_i in y
   s += f(y_i)
end

```

---

<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 23, 2023, 1:12pm UTC](https://discourse.julialang.org/t/macro-to-choose-macros/96507/2 "2023-03-23T13:12:22Z")

</div>

Macros are just code rewriters. So the first thing to do is to decide what code you would write by hand, without the macro. For example, maybe you want:

```julia
@always_fast foo

```

to turn into

```julia
if Threads.nthreads() > 1
    Threads.@threads foo
else
    @simd foo
end

```

which could be accomplished with:

```julia
macro always_fast(expr)
    quote
        if Threads.nthreads() > 1
            Threads.@threads $(esc(expr))
        else
            @simd $(esc(expr))
        end
    end
end
```

---

<div class="post-metadata">

### Author: ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)
#### Post date: [March 23, 2023, 1:35pm UTC](https://discourse.julialang.org/t/macro-to-choose-macros/96507/3 "2023-03-23T13:35:47Z")

</div>

Thanks. Unfortunately

```julia
macro always_fast(expr)
    quote
        if Threads.nthreads() > 1
            @inbounds @batch per=thread $(esc(expr))
        else
            @inbounds @simd $(esc(expr))
        end
    end
end
y = rand(100,100)
x = similar(y)
@always_fast for I ∈ eachindex(x,y)
    x[I] = sin(y[I])
end

```

gives the error  
`ERROR: LoadError: ArgumentError: @threads requires a `for` loop expression`

This is the problem I was having. Both `@simd` and `Threads.@threads` expect a for loop so this syntax for the macro doesn’t work.

---

<div class="post-metadata">

### Author: ![mstewart](https://avatars.discourse-cdn.com/v4/letter/m/b5a626/32.png) [@mstewart](https://discourse.julialang.org/u/mstewart)
#### Post date: [March 23, 2023, 1:57pm UTC](https://discourse.julialang.org/t/macro-to-choose-macros/96507/4 "2023-03-23T13:57:10Z")

</div>

I’m not great at macros myself, so take my reply with a grain of salt, but I had a similar problem. I think the source is that some macros don’t handle escaped expressions. I think it was `@turbo` where this happened to me. I ended up doing something analogous to

```julia
macro always_fast(expr)
    esc(quote
        if Threads.nthreads() > 1
            @inbounds @batch per=thread $(expr)
        else
            @inbounds @simd $(expr)
        end
    end)
end

```

I think that’s OK for this, but in theory for more complicated code you would presumably have to worry about variable capture. So it doesn’t seem like a very good solution in general. I would also be interested if there’s some better approach.

---

<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 23, 2023, 2:00pm UTC](https://discourse.julialang.org/t/macro-to-choose-macros/96507/5 "2023-03-23T14:00:55Z")

</div>

> [@mstewart](#):
>
> I think the source is that some macros don’t handle escaped expressions.

Right, the `esc` was the problem. Putting the `esc` outside the `quote` should be fine here since your macro doesn’t introduce any new variables that you would have to hygienize.

Otherwise, you could check `Meta.isexpr(expr, :for)` and then (for `for` loops) create a new `for` loop expression where you escape the arguments/body.

---

<div class="post-metadata">

### Author: ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)
#### Post date: [March 24, 2023, 3:59am UTC](https://discourse.julialang.org/t/macro-to-choose-macros/96507/7 "2023-03-24T03:59:50Z")

</div>

> [@weymouth](#):
>
> ```julia
> s = 0
> @always_fast for y_i in y
> s += f(y_i)
> end
> 
> ```

Note that `Threads.@threads` operates by converting the loop body into a function. To satisfy Julia’s closure semantics, the capture `s` is boxed in a mutable container on the heap. This container is type-unstable as per issue [#15276](https://github.com/JuliaLang/julia/issues/15276), causing runtime dispatch. So, your always-fast loop may not always be fast.

Consider this instead:

```julia
s = Threads.Atomic{Int}(0)
@always_fast for y_i in y
    Threads.atomic_add!(s, y_i)
end
println(s[])

```

---

<div class="post-metadata">

### Author: ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)
#### Post date: [March 24, 2023, 8:35am UTC](https://discourse.julialang.org/t/macro-to-choose-macros/96507/8 "2023-03-24T08:35:19Z")

</div>

Good point! Perhaps it would be best to outsource reductions. I see some old questions about this on discourse, but nothing recent and no consensus. Is there a solid multi-threaded reduce operator in a Julia package?
