Search code examples
c#multithreadingprocessor

What is the better number of threads running simultaneous for specific processor?


I have a computer with an 8-core processor, and I have a doubt about what's the max number of threads (software, don't thread of processor.) that my computer needs to use the max potencial of your 8-core.

I'm creating 160 threads simultaneous, so each core of my processor will process about ~20 threads, is it correct?

My questions are:

  • 20 threads by core of processor is a good number?
  • Independent the number of threads running, its will be divided equality by the number of cores?
  • How to know whats the better number of threads according with my processor?

Solution

  • I'm creating 160 threads simultaneous, so each core of my processor will process about ~20 threads, is it correct?

    Not quite. Each core can only process a single thread at a time. When the OS decides the thread had enough of the spotlight, it will switch it out for another thread. If you can do the same work in 8 threads, you're going to have the same throughput plus the savings you get from avoiding unnecessary context switching.

    20 threads by core of processor is a good number?

    No. One thread per core is the spot. Hyperthreaded cores count for two, approximately - how well that works varies.

    Independent the number of threads running, its will be divided equality by the number of cores?

    No. This is decided by the operating system, which will tend to take many different things in its decision-making. Also, threads which are blocking will not execute anywhere. It's actually quite possible for a few of those threads to hog much more CPU time than others.

    How to know whats the better number of threads according with my processor?

    Use higher level constructs than threads. That means using the thread pool for simple work items, Parallel.For and family for "synchronous" paralellism, and asynchronous Tasks for complex asynchronous work tasks (including thread pool fragments). There really aren't many good reasons to manually create threads anymore - unless you really know what you're doing, and why it matters to have it on a dedicated thread.

    The critical point is this only applies to CPU work. You can easily have a single thread handling a hundred separate asynchronous tasks at the same time. This is especially easy to work with when using await against asynchronous APIs. Single thread per CPU can easily get you 100% utilization as long as it actually does CPU work. If it doesn't, you want to use asynchronous I/O anyway - there's no point in wasting threads (accompanied with their cost in memory, switching overhead, overtasking the scheduler, garbage collection...) while waiting.

    The typical example would be an asynchronous web service dealing with some database work:

    string GetUserName(Guid userId)
    {
      return Db.GetUser(userId).Name;
    }
    

    This synchronous method will take up the request thread while processing the DB request. If your DB requests takes a second, and you have 2000 requests at the same time, you'll need 2000 threads handling the requests. However, DB requests are just asynchronous I/O - the thread is basically waiting for the response from the DB to come back. The thread is wasted waiting.

    Instead, you could use an asynchronous API:

    async Task<string> GetUserName(Guid userId)
    {
      var user = await Db.GetUserAsync(userId);
    
      return user.Name;
    }
    

    The code is almost identical, but the await construct doesn't actually block the request thread - it basically says "Okay, I'm done, you can use this thread for something else. I'll ping you back when I'm ready to continue.". This patterns means that you never need more threads than CPU cores - if the CPU is at 100%, there's no point in adding more threads to handle the load. If it's not, it means some thread isn't doing any CPU work - which is fine, it will be used when another piece of work comes around. And now, instead of having 2000 threads handling 2000 requests, you have 8 threads handling the same 2000 requests.