How to use a variable value inside @tullio

What’s the proper syntax to get this to run?

using Tullio


function ugh(tf::Bool)
    X = randn(10000,100)
    y = zeros(100)
    @tullio threads=tf y[j] = X[i,j]
end

ugh(true)

What exactly is your question? Is it about passing a flag to threads or is it about the @tullio expression itself?

about passing a flag to threads (I know I can just pass true' or false’)

Alright. I’d probably do it like this:

if tf
   @tullio blah = blah
else
    @tullio blah = blah threads=false
end

I think that should probably work.

1 Like

Yeah, sure. But there’s no way to do it on the line itself? I have a lot of @tullio statements.

Not sure. I think because of how the function gets compiled it needs the value when it is created, not later. Perhaps someone with more macro knowledge can chime in. But if you do what I suggested then I think the compiler can just create two branches and smartly pick the right one.

Are you sure you need to be manually switching threads like this?

I’ll settle for a switch (bool constant) at the top of the file.

It looks like there are also some global options available in Tullio. Perhaps you can turn off threading for all of them at once.

1 Like

I never thought to try this, but the reason it doesn’t work is that values other than true/false are interpreted differently. The default behaviour does this:

@tullio y[j] = X[i,j] verbose=2
...
(Tullio.threader)(𝒜𝒸𝓉!, (Tullio.storage_type)(y, X), y, tuple(X), tuple(𝒶𝓍j), tuple(𝒶𝓍i), +, 262144, nothing)

where the argument 262144 is the macro’s estimate of how big X must be to justify multi-threading, based on the function not containing log etc. Which threader then uses once the actual size of X is known.

The option threads=false replaces that with nothing, which disables threader completely. But anything else is assumed to be a different threshold:

@tullio threads=tf y[j] = X[i,j] verbose=2
...
(Tullio.threader)(𝒜𝒸𝓉!, (Tullio.storage_type)(y, X), y, tuple(X), tuple(𝒶𝓍j), tuple(𝒶𝓍i), +, tf, nothing)

So a possible hack would be to write threads=ts where ts == tf ? nothing : 100^2. But it’s possible that this design ought to change.

Indeed, if you write @tullio threads=false at the top of the file, then all subsequent macro calls will use that. But these global macro options affect the parse-time behaviour, not the running of already-parsed code.

5 Likes

Awesome! Thanks for the elaborate explanation. Perhaps, being able to pass it as a command line argument would be helpful.

The reason I wanted this is that sometimes I want to run a routine once, in which case I want to use the parallel version of Tullio, and sometimes I want to do a Monte Carlo simulation in which the routine gets called a few thousand times and the parallelization is more effectively done in the outer loop.

I would guess that if the problem is big enough to benefit from multithreading when run once, then the overhead of unnecessary nthreads @spawn calls ought not to be large. I think Julia’s threading is fairly smart about this. But have not timed it extensively, and could be wrong about this.

Note that threads=false at present also disables memory tiling, which depending on the problem can make a big difference (especially things which reduce over the last index). threads=typemax(Int) is an ugly way to have tiles but declare no problem big enough to need two threads.

2 Likes

Re unnecessary @spawn calls, I’ll run it once it’s done later this week and report here.