# @threads uses only half the number of nthreads()

**URL:** <https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058>\
**Category:** General Usage\
**Tags:** question, multithreading, threads\
**Created:** [January 27, 2021, 2:29pm UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058 "2021-01-27T14:29:56Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![msekino](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/msekino/32/12889_2.png) [@msekino](https://discourse.julialang.org/u/msekino)\
**Post date:** [January 27, 2021, 2:29pm UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/1 "2021-01-27T14:29:56Z")

</div>

Recently I built a PC with Ryzen Threadripper 3990X, which has 64 cores and 128 threads.  
The OS is Windows 10 Pro.

When I tried the code below, only 64 threads reached 100% as shown in the attached image.  
Why aren’t all the threads being used?

```julia
julia> using Base.Threads
julia> nthreads()
128
julia> @threads for i in 1:10000 rand(100000) end

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/3/c/3caeb11cedaa2f3240d9d85a1142068835795e32.png)

---

<div class="post-metadata">

**Author:** ![MatFi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/matfi/32/10002_2.png) [@MatFi](https://discourse.julialang.org/u/MatFi)\
**Post date:** [January 27, 2021, 6:14pm UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/3 "2021-01-27T18:14:21Z")

</div>

> [@msekino](#):
>
> @threads for i in 1:10000 rand(100000) end

this takes 0.8 s with 16 threads on my 4900HS, can imagine that your 3990X is simply to fast for

What happens when you generate more random numbers?

```julia
@threads for i in 1:10000 rand(100000,50) end

```

---

<div class="post-metadata">

**Author:** ![ranocha](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ranocha/32/35588_2.png) [@ranocha](https://discourse.julialang.org/u/ranocha)\
**Post date:** [January 27, 2021, 6:44pm UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/4 "2021-01-27T18:44:51Z")

</div>

If you’re bound by computational power, using more threads than CPU cores usually does not help - since each core can only be busy with one thread at a time. Such hyperthreading can help if a task is waiting for something, but is usually does not help in compute-bound scenarios.

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [January 27, 2021, 8:33pm UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/5 "2021-01-27T20:33:20Z")

</div>

Something I would try is run a second instance of julia in a new terminal window and run the @threads command in both. Does that use all 128 cores? If it doesn’t then check your BIOS to see if hyperthreading is disabled. Or check windows, maybe your version doesn’t use more than 64 cores?

---

<div class="post-metadata">

**Author:** ![msekino](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/msekino/32/12889_2.png) [@msekino](https://discourse.julialang.org/u/msekino)\
**Post date:** [January 28, 2021, 2:15am UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/6 "2021-01-28T02:15:37Z")

</div>

@pixel27

> Or check windows, maybe your version doesn’t use more than 64 cores?

Thankyou! Now, I found the following article.

> **[AMD defends Threadripper 3990X – it’s not slower on Windows 10 Pro, the...](https://www.techradar.com/news/amd-defends-threadripper-3990x-its-not-slower-on-windows-10-pro-the-reviews-are-wrong)**
>
> It seems the problem might be down to the exact version of the OS benchmarked

> AMD has come forward to clarify issues around its Ryzen Threadripper 3990X flagship 64-core HEDT processor, which as you may have seen, has recently been reported as not running with its full capabilities on Windows 10 Pro – due to the OS not being able to handle 128-threads.  
> …  
> AMD sent out a statement as follows: “Higher editions/versions of Windows 10 confer no additional performance or compatibility benefits to the processor. We understand that this suggestion has been made in the media, but we believe this to be an error in testing that our team is presently diagnosing.”

I will check the version of my own, update and try again!

PS: Hmmm, my Windows 10 Pro is already the newest version…

---

<div class="post-metadata">

**Author:** ![msekino](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/msekino/32/12889_2.png) [@msekino](https://discourse.julialang.org/u/msekino)\
**Post date:** [January 28, 2021, 2:30am UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/7 "2021-01-28T02:30:57Z")

</div>

@ranocha  
I see! I didn’t know much about hyperthreading.  
So, the CPU may be using its full capacity even though it is apparently using only 64 threads (when this is the case using 64 cores.).

---

<div class="post-metadata">

**Author:** ![msekino](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/msekino/32/12889_2.png) [@msekino](https://discourse.julialang.org/u/msekino)\
**Post date:** [January 28, 2021, 4:08am UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/8 "2021-01-28T04:08:17Z")

</div>

@MatFi

> this takes 0.8 s with 16 threads on my 4900HS, can imagine that your 3990X is simply to fast for

Oh… With my 3990X, it takes 2.5 seconds…

```julia
julia> @benchmark @threads for i in 1:10000 rand(100000) end

BenchmarkTools.Trial:
  memory estimate: 7.45 GiB
  allocs estimate: 20643
  --------------
  minimum time: 1.698 s (26.38% GC)
  median time: 2.487 s (37.38% GC)
  mean time: 4.204 s (62.77% GC)
  maximum time: 8.427 s (77.59% GC)
  --------------
  samples: 3
  evals/sample: 1

```

---

<div class="post-metadata">

**Author:** ![msekino](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/msekino/32/12889_2.png) [@msekino](https://discourse.julialang.org/u/msekino)\
**Post date:** [January 28, 2021, 4:24am UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/9 "2021-01-28T04:24:28Z")

</div>

Now I tried to turn off SMT, 64 cores and 64 threads (logical cpu cores).  
The result was better than before.

```julia
julia> @benchmark @threads for i in 1:10000 rand(100000) end

BenchmarkTools.Trial:
  memory estimate: 7.45 GiB
  allocs estimate: 20324
  --------------
  minimum time: 944.958 ms (32.53% GC)
  median time: 1.006 s (32.18% GC)
  mean time: 1.231 s (36.41% GC)
  maximum time: 1.885 s (42.55% GC)
  --------------
  samples: 5
  evals/sample: 1

```

I would like to use this for a while.  
Thank you for everyone! @MatFi @ranocha @pixel27

---

<div class="post-metadata">

**Author:** ![Chris\_Green](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chris_green/32/32265_2.png) [@Chris\_Green](https://discourse.julialang.org/u/Chris_Green)\
**Post date:** [October 11, 2021, 9:42pm UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/11 "2021-10-11T21:42:21Z")

</div>

This issue is almost certainly because of windows processor thread groups.

On dual socket systems, or more than 64 threads, by default, threads created in windows by a process will all be placed on the same physical core or subset of the threads.

This problem is easy to work around with a little code in thread creation in c++, and I have writtent his code for my c++ code. If I can figure out how to put it in Julia for windows, I’ll take a stab at it.

I have a 128 core/256 thread AMD system. Julia threads on it will only use 1/4 of the cores :-(.

If anyone could help me with where I could code this, its a simple loop that iterates the thread handles and does a couple windows calls.  
Perhaps it can be done wittout modifying julia or its libraries at all, if I can iterate the windows htread handles and make some calls into the windows dlls.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 11, 2021, 10:16pm UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/12 "2021-10-11T22:16:57Z")

</div>

this benchmark is RAM bottlenecked.

> [@Chris\_Green](#):
>
> Julia threads on it will only use 1/4 of the cores :-(.

what does `Threads.nthreads()` say? Also MWE snippet? (probably in a new thread)

---

<div class="post-metadata">

**Author:** ![Chris\_Green](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chris_green/32/32265_2.png) [@Chris\_Green](https://discourse.julialang.org/u/Chris_Green)\
**Post date:** [October 12, 2021, 12:58am UTC](https://discourse.julialang.org/t/threads-uses-only-half-the-number-of-nthreads/54058/13 "2021-10-12T00:58:07Z")

</div>

This is a well known misfeature of the windows OS that needs a minor bit of code at thread creation time to work around. thread libraries like intel TBB do this for you, but otherwise you need a few lines of code.  
You can see an article about it here, including how to fix it in c code:

> **[Thread pools and Windows processor groups](https://chrisgreendevelopmentblog.wordpress.com/2017/08/29/thread-pools-and-windows-processor-groups/)**
>
> I needed to write a basic worker thread pool implementation. I needed a simple system that let me queue jobs, have those jobs executed by worker threads, and wait on job completion. I looked at a f…

In my test case, there are 250 windows threads created by Julia as I asked, perhaps by using pthreads which I assume lacks this code as well.

When they run in a tight loop that is not memory bound, you will see 256 non-idle threads, but only 1/4 CPU usage in the task manager. Using any good windows diagnostic tool, you can see that julia has created 250 threads, but they are **all assigned to the same thread group** instead of using the 4 groups that are present in this machine. _Thus, the 256 OS threads only use 64 HW threads out of the 256 available_

If you run a third party program called “process lasso”, it can be told to “spread the threads out over all the processor groups” for a running process. Applying this to julia makes it able to take advantage of all the cores. You will see that now the process manager shows 100% CPU usage on all cores, and that you get corresponding speedups.

I managed to get a 150x speedup vs single threaded code on this 128/256 core system by doing this.

Memory bandwidth doesn’t figure into this. Even if the threads were horribly memory bound, they would consume 100% of all of the cpus, though they might be spending more time servicing cache misses then doing math, and so not see a scalable speedup.

Its an easy problem to repro, and an easy one tix if you have a system to test on.
