Threading issue - working on a view of a bool array

Appears to be an issue for me on both Julia 1.1 and 1.3.alpha-0. Not tried other builds.
I am using a array of falses AList=(falses(n)) and A=view(AList) inside the the threaded loop.

See the following repo:
https://github.com/Timmmdavis/JuliaThreadIssue

using:

using Dislocations
include("...\test\TestThreads.jl")

This loops some threaded code, around 20 loops inside this test for me (sometimes more sometimes less it) it fails. If I turn threads off (line 75 of Triangular.jl) it gives me the expected result. If not (threads are on) this fails, I reset the viewed array on line 454-458 so it shouldnt matter…?. If inside the threaded loop I turn on lines 83-85 (this just inits the falses array inside the loop) then this works fine (kind of expected).

Any ideas?

(on Windows 64 bit versions, not tried Linux)

I have not looked at your example in detail. However, falses(sz) creates a BitArray, not a bool array.

Your CPU is capable of byte (i.e. Bool)-sized reads and writes, and access to bool arrays is therefore threadsafe (as long as you avoid actual data races, no new ones will be introduced; still, your CPU will slow down to a crawl for parallel not-read-only access that shares a cache line).

No sensible CPU is capable of bit addressing. BitArray store 64 bit in one integer, and bits that share a single UInt64 in storage alias. This means that @threads for i in 1:n A[i] = rand(Bool) end will have race conditions if A is a BitArray (unless there are some divisibility conditions between nthreads(), length(A) and 64).

BitArray is not thread-safe unless you either access read-only, or know very precisely what you are doing.

Nice, thanks for the detailed reply.

Yes, it’s a bit array not a bool array, I was being naïve. I also hadn’t realised this was complex for BitArrays. Are there plans to make this thread safe for the 1.3 build? I got the impression this was supposed to be thread safe? JuliaCon 2019 | Multi-threading in Julia with PARTR - YouTube ~5mins. I tried both with:

[Threads.@threads](mailto:Threads.@threads)

[Threads.@spawn](mailto:Threads.@spawn)

On the 1.3.alpha.

Could you also tell me what the divisibility conditions are for a thread safe BitArray? I tried

nthreads() = 4

length(A) = [64 by n]

where A is my 64 bit BitArray, I view each column (a length 64 vector of A) inside the threaded loop and it still throws an error.

Cheers,

Tim

Safe intervals are (1+64*n) : 64*(n+1), and (1+64*n) : end. You would need to cut up your range in a way that all intervals are safe. Do this yourself, such that you get nthreads() many ranges, and then you can run Threads.@threads for ran in ranges. You could read the source code for Threads.@threads and reverse engineer the exact conditions under which it will split up your range in a safe way. However, I recommend against relying on these internals (likely to break in the near future, unmaintainable).

No. You need to change from BitArray{N} to Array{Bool, N}. This is my recommendation for this kind of issue (fill(false, n) instead of falses(n)).

2 Likes

Perfect, a nice simple solution. Cheers,