# Macro to turn on/off parallelisation

**URL:** <https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808>\
**Category:** General Usage\
**Tags:** macros, parallel, multithreading\
**Created:** [August 4, 2024, 12:50pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808 "2024-08-04T12:50:47Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![jibe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jibe/32/46134_2.png) [@jibe](https://discourse.julialang.org/u/jibe)\
**Post date:** [August 4, 2024, 12:50pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/1 "2024-08-04T12:50:47Z")

</div>

I want to define a macro, `@parallel`, to turn on/off parallelisation in `for` loops, depending on a command-line argument. Here, parallelisation is embarassing and uses `Threads.@threads`.

Why would I want to do this? Because I want to fully turn off multi-threading when checking for unwanted allocations. And `Threads.@threads` has spurious allocations.

Here is a MWE:

```julia
# Reading the command-line arguments
using ArgParse
tabargs = ArgParseSettings()
@add_arg_table! tabargs begin
    "--parallel"
    help = "Parallelisation: true/false"
    arg_type = Bool
    default = false
end
parsed_args = parse_args(tabargs)
const PARALLEL = parsed_args["parallel"]
#####
if PARALLEL
    macro parallel(ex::Expr) # We want to use multiple threads
        return :( Threads.@threads $(ex) )
    end
else
    macro parallel(ex::Expr) # No multi-threading
        return :( $(ex) )
    end
end
#####
# Using the macro in a function
function run!()
    @parallel for i=1:2
        sleep(1)
    end
end
#####
@time run!()

```

The code can then be tested in various regimes via  
`julia code.jl --parallel false` (Time: 2s)  
`julia code.jl --parallel true` (Time: 2s)  
`julia -t 1 code.jl --parallel false` (Time: 2s)  
`julia -t 1 code.jl --parallel true` (Time: 2s)  
`julia -t 2 code.jl --parallel false` (Time: 2s)  
`julia -t 2 code.jl --parallel true` (Time: 1s)

Hence, the definition of the macro seems (reasonably) correct. Yet, when used in my real test case, it leads to unexpected errors. I do not get these errors when using directly `Threads.@threads`. So, `@parallel` seems badly defined.

What would be the most idiomatic (and correct) way of implementing this macro in `julia`?

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [August 4, 2024, 1:28pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/2 "2024-08-04T13:28:10Z")

</div>

I’m not by my computer so forgive me if this code isn’t quite right. But you could do something like

```julia

Threads.foreach(iter; nworkers=PARALLEL ? Threads.nthreads() : 1) do x
# iter code here
end

```

No macro needed!

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [August 4, 2024, 2:12pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/3 "2024-08-04T14:12:29Z")

</div>

> [@mrufsvold](#):
>
> I’m not by my computer so forgive me if this code isn’t quite right.

Unfortunately, the syntax of `Threads.foreach` is indeed a bit different:

```julia
Threads.foreach(f, channel::Channel;
                  schedule::Threads.AbstractSchedule=Threads.FairSchedule(),
                  ntasks=Threads.threadpoolsize())

```

I’m sure you can use this with a proper `Channel` and switch between sequential and parallel processing via `ntasks`, but `Threads.foreach` seems a bit complicated.

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [August 4, 2024, 2:16pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/4 "2024-08-04T14:16:19Z")

</div>

I forgot it was for channels only!

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [August 4, 2024, 2:18pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/5 "2024-08-04T14:18:36Z")

</div>

> [@jibe](#):
>
> Hence, the definition of the macro seems (reasonably) correct. Yet, when used in my real test case, it leads to unexpected errors

It’s hard to find the problem when the code you post runs fine. 🙂

My guess is that the issue you’re encountering is related to [macro hygiene](https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene). For example,

```julia
macro parallel(ex::Expr)
    return :(Threads.@threads $ex)
end

function run!()
    sleep_time = 1.
    @parallel for i = 1:2
        sleep(sleep_time)
     end
end

run!()

```

throws an error:

```julia
ERROR: TaskFailedException

    nested task error: UndefVarError: `sleep_time` not defined
    ...

```

Changing to macro to

```julia
macro parallel(ex::Expr)
    return esc(:(Threads.@threads $ex))
end

```

and keeping the rest of the code the same, everything now runs just fine.

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [August 4, 2024, 2:28pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/6 "2024-08-04T14:28:56Z")

</div>

A somewhat similar approach is suggested [here](https://discourse.julialang.org/t/optionally-multi-threaded-for-loop/81902/8) by `oxinabox`: use `Base.foreach` for sequential and `ThreadsX.foreach` for parallel execution (and automatically switch based on the value of `PARALLEL`).

@jibe Check out the linked topic: it deals with the same question and has some more answers.

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [August 4, 2024, 6:57pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/7 "2024-08-04T18:57:43Z")

</div>

One more thought: if you want to use the function proposed in that link, you could still compile away the if statement without the macro.

```julia
if PARALLEL
    _foreach =ThreadsX.foreach
else
    _foreach = Base.foreach
end

```

Now, you can use `_foreach` everywhere, and it’ll reference the correct function.

That said, You probably shouldn’t be making a threading decision in a very hot part of the code, so compiling away that `if` doesn’t really matter.

---

<div class="post-metadata">

**Author:** ![jibe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jibe/32/46134_2.png) [@jibe](https://discourse.julialang.org/u/jibe)\
**Post date:** [August 4, 2024, 7:45pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/8 "2024-08-04T19:45:58Z")

</div>

Fantastic! This does get the job done, no errors are left, and I got numerous links/references to mull over 😉  
In practice, for the case `PARALLEL == false`, should I also make the change to

```julia
macro parallel(ex::Expr) # No multi-threading
    return esc(:( $(ex) ))
end

```

?

---

<div class="post-metadata">

**Author:** ![jibe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jibe/32/46134_2.png) [@jibe](https://discourse.julialang.org/u/jibe)\
**Post date:** [August 4, 2024, 7:55pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/9 "2024-08-04T19:55:46Z")

</div>

Thank you very much for your suggestion. Unfortunately, in the case `PARALLEL == false`, although the code would use a single thread, it would use `Threads.foreach` and not `Base.foreach`, hence making unwanted allocations?

---

<div class="post-metadata">

**Author:** ![jibe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jibe/32/46134_2.png) [@jibe](https://discourse.julialang.org/u/jibe)\
**Post date:** [August 4, 2024, 8:02pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/10 "2024-08-04T20:02:38Z")

</div>

One last newbie question 🙂  
In that piece of code, is it a good practice to add a `const` [in particular if in global scope] to read

```julia
if PARALLEL
    const _foreach = ThreadsX.foreach
else
    const _foreach = Base.foreach
end

```

This may prevent useless allocations when calling `_foreach`?

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [August 4, 2024, 8:06pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/11 "2024-08-04T20:06:59Z")

</div>

Not a newb question – you’re absolutely right that you should do that!

Even if it didn’t affect allocations, always good to const a global if you don’t intend to change it.

I originally wrote this as defining a new function which calls either base or ThreadsX. Then change my mind because that would be an unnecessary pollution of the stack trace, But when I switched to just aliasing the functions, I forgot to add const.

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [August 4, 2024, 8:10pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/12 "2024-08-04T20:10:57Z")

</div>

> [@jibe](#):
>
> `Threads.foreach` and not `Base.foreach`

I don’t understand your question here. In the case where PARALLEL is false, it will call `Base.foreach`, which is serial. Could you clarify what you mean?

---

<div class="post-metadata">

**Author:** ![jibe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jibe/32/46134_2.png) [@jibe](https://discourse.julialang.org/u/jibe)\
**Post date:** [August 4, 2024, 8:22pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/13 "2024-08-04T20:22:04Z")

</div>

You’re right and my wording was confusing – my apologies. Here, I was referering to your initial attempt reading

```julia
Threads.foreach(iter; nworkers=PARALLEL ? Threads.nthreads() : 1) ...

```

There, one always uses `Threads.foreach`, even for `PARALLEL == false`. I believe that this would not have been ideal. Indeed, a threaded loop with a single thread is not the same as a “base” loop. Would you agree?

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [August 4, 2024, 8:22pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/14 "2024-08-04T20:22:47Z")

</div>

Indeed. You can again check that

```julia
macro parallel(ex)
    return ex
end

```

would throw an `ERROR: UndefVarError: `sleep_time` not defined`, because all variables in `ex`, in casu `sleep_time`, have essentially been automatically made local, so that Julia does not recognise them as coming from the surrounding scope.

```julia
macro parallel(ex)
    return esc(ex)
end

```

(which is the same as your `esc(:($(ex)))`, but a bit shorter) will again run fine.

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [August 4, 2024, 8:24pm UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/15 "2024-08-04T20:24:10Z")

</div>

Yeah, I think you should forget my first message for two reasons:

1. As you pointed out, it will still spawn tasks. On single threaded.
2. More importantly, it only works with Channels.

---

<div class="post-metadata">

**Author:** ![carstenbauer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/carstenbauer/32/4981_2.png) [@carstenbauer](https://discourse.julialang.org/u/carstenbauer)\
**Post date:** [August 5, 2024, 4:50am UTC](https://discourse.julialang.org/t/macro-to-turn-on-off-parallelisation/117808/16 "2024-08-05T04:50:32Z")

</div>

FWIW, OhMyThreads.jl has a `SerialScheduler` that you can pass to `tforeach` and co that turns off any multihreading.
