Error in Using "@threads"

Hello,

I have a julia file called “makeNetlistTest.jl” which calls function “DefineYnImproved()” and basically the execution works properly.

However, when I am trying to use “@threads” before the “for loop” in function “DefineYnImproved”, I am having error, as seen in the pictures:


Could you please guide me for the problem and solution?

Thank you!

You’re getting a BoundsError, which I don’t think has anything to do with the @threads macro, unless you’re doing something not thread-safe like reading from and writing to the same memory location on different threads. Unfortunately, the line that errored (31) is not included in your screenshot, so I can’t say much else.

It might be helpful to condense your code into a minimum working example (MWE) that reproduces the error. Or at the least, post the full DefineYnImproved method in a code block, i.e.,

```julia
Your code here
```

I think it’s also recommended to copy full error messages and display them in a code block as well.

1 Like

Line 31 is

                    Yn[i,netlist.cNeg[m]] += - (2 .* netlist.c[m]) ./ Dt;

This is the whole error message:

  0.284914 seconds (549.45 k allocations: 33.806 MiB, 53.39% compilation time)

ERROR: LoadError: TaskFailedException
Stacktrace:
 [1] wait
   @ .\task.jl:322 [inlined]
 [2] threading_run(func::Function)
   @ Base.Threads .\threadingconstructs.jl:34
 [3] macro expansion
   @ .\threadingconstructs.jl:93 [inlined]
 [4] DefineYnImproved()
   @ Main c:\Work\From Javad\Whole Package\EMTJulia\DefineYnImproved.jl:6
 [5] top-level scope
   @ .\timing.jl:210

    nested task error: BoundsError: attempt to access 10-element Vector{Int64} at index [10]
    Stacktrace:
     [1] getindex
       @ .\array.jl:801 [inlined]
     [2] getindex(A::SparseMatrixCSC{Float64, Int64}, i0::Int64, i1::Int64)
       @ SparseArrays C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\SparseArrays\src\sparsematrix.jl:2134
     [3] macro expansion
       @ c:\Work\From Javad\Whole Package\EMTJulia\DefineYnImproved.jl:12 [inlined]
     [4] (::var"#41#threadsfor_fun#6"{UnitRange{Int64}})(onethread::Bool)
       @ Main .\threadingconstructs.jl:81
     [5] (::var"#41#threadsfor_fun#6"{UnitRange{Int64}})()
       @ Main .\threadingconstructs.jl:48
in expression starting at c:\Work\From Javad\Whole Package\EMTJulia\makeNetlistTest.jl:120

I noticed that the error message you pasted above errored on a different line: line 12

Yn[i,i] += Dt ./ (2*netlist.l[m]);

instead of line 31.

Because each thread is working with a different i, the increment to Yn should be thread-safe.

Looking more closely at the offending lines and the stack trace, it looks like Yn[i,i] (line 12) or Yn[i,netlist.cNeg[m]] (line 31) are being indexed out of bounds:

(The other indexing operations in those lines are on Vectors, not Matrixes.) So it appears to me that multithreading is not to blame, but you are initializing Yn in some way that doesn’t mesh with nbnodes and/or netlist.cNeg[m]. It’s hard to say more than this without knowing anything about Yn, nbnodes, and netlist, which appear to be global variables.

1 Like

Please refer to above

Line 31

seems to have errored because netlist.cNeg[2] is 0. I’m not sure why line 12

errored. My only guess is that you accidentally updated nbnodes without recreating Yn.

1 Like

As you can see, it is not 0.
As a kindly reminder, the code worker properly when I dont use @threads.

Right, netlist.cNeg is not 0, but its second element is.

julia> a = [8 0]; a[2]
0

Yet it seems like I was wrong in my assessment, because I just copied and pasted all your code into the REPL and it ran to completion without any errors. (This is with @threads.) I’m not sure why it’s not working for you. My only guess is that you accidentally modified some global state during your testing. Either that or there really is some multithreading issue that isn’t easily reproduced.

1 Like

Just recently, I found that the code with @threads, runs a very few sometimes and doesnot run very often. It is quite confusing…
each thread works on different row of Yn matix, so I think it is already thread-safe.

It’s not thread safe, here’s a smaller demonstration:

julia> using SparseArrays

julia> x = spzeros(1000)
1000-element SparseVector{Float64, Int64} with 0 stored entries

julia> x.nzind
Int64[]

julia> Threads.@threads for i in 1:2:1000
         x[i] = Threads.threadid()
       end
ERROR: TaskFailedException
Stacktrace:
 [1] wait
   @ ./task.jl:322 [inlined]
 [2] threading_run(func::Function)
   @ Base.Threads ./threadingconstructs.jl:38
 [3] top-level scope
   @ ./threadingconstructs.jl:97

    nested task error: BoundsError: attempt to access 545-element Vector{Float64} at index [85]
    Stacktrace:
     [1] _growat!
       @ ./array.jl:924 [inlined]
     [2] insert!
       @ ./array.jl:1370 [inlined]
     [3] setindex!(x::SparseVector{Float64, Int64}, v::Float64, i::Int64)
       @ SparseArrays ~/.julia/dev/julia/usr/share/julia/stdlib/v1.8/SparseArrays/src/sparsevector.jl:334
     [4] setindex!(x::SparseVector{Float64, Int64}, v::Int64, i::Int64)
       @ SparseArrays ~/.julia/dev/julia/usr/share/julia/stdlib/v1.8/SparseArrays/src/sparsevector.jl:340
     [5] macro expansion
       @ ./REPL[39]:2 [inlined]
     [6] (::var"#137#threadsfor_fun#10"{UnitRange{Int64}})(onethread::Bool)
       @ Main ./threadingconstructs.jl:85
     [7] (::var"#137#threadsfor_fun#10"{UnitRange{Int64}})()
       @ Main ./threadingconstructs.jl:52

julia> x.nzind
333-element Vector{Int64}:
   1
   3
   5
   7
...

Sparse arrays just list of indices and a vector of data, and when you write into them, they grow these lists.

2 Likes

Do mean that threads will let the Sparse arrays grow the lists improperly, such as some threads will overwrite on the same indices? but the answer below seems to be correct, right?

It it finished, it would have values at all 1:2:1000, hence length 500. On that run it crashed 2/3 of the way, but this will vary.

Yes. In a normal dense array there are pre-existing places to write. In a sparse array there are not, any write may need to move other data around, or grow an array (i.e. copy it to a new, larger, block of memory), and if different threads try this you may get wrong answers even if you don’t get an error.

This is what it stores

julia> x = sprand(5, 0.5)
5-element SparseVector{Float64, Int64} with 2 stored entries:
  [1]  =  0.836658
  [4]  =  0.0097166

julia> dump(x)
SparseVector{Float64, Int64}
  n: Int64 5
  nzind: Array{Int64}((2,)) [1, 4]
  nzval: Array{Float64}((2,)) [0.8366576714961177, 0.009716596005340361]

julia> x[2] = 222.2;

julia> dump(x)
SparseVector{Float64, Int64}
  n: Int64 5
  nzind: Array{Int64}((3,)) [1, 2, 4]
  nzval: Array{Float64}((3,)) [0.8366576714961177, 222.2, 0.009716596005340361]
1 Like

Understood and thank you very much!
So, there is no way to use Sparse Array with @thread, right?

sure there is, you just need to get a mutex before modifying it.

Could you give me an example please?

It’s not necessarily going to be useful in your example program, but if you’re doing a calculation that’s very long and then storing the result in a sparse array. then you could do something like

c = Base.Threads.Condition()

@threads for i in 1:1000
   val = do_some_calc(i)
   lock(c)
   mysparsearray[i] = val
   unlock(c)
end

This would work well if do_some_calc takes a while so that most of the time is spent there and not much time is spent waiting for the lock

1 Like

Thanks, but it seems that using val variable is not thread-safe, right?

I would have to read up but I think each thread gets its own local variables

1 Like