views:

433

answers:

4

I have an application which performs 30 independent tasks simultaneously using multithreading, each task retrieves data over http, performs a calculation and returns a result to the ui thread.

Can I use TPL to perform the same tasks?

Does TPL create 30 new threads and spread them over all the available cores, or does it just split the tasks over the available cores and use one thread per core?

Will there be a performance boost using TPL over multithreading in this case?

+3  A: 

I believe TPL will usually use one thread per core unless you specifically tell it to use more. It's possible that it will detect when that's not enough - e.g. in your case, where your tasks are going to spend most of their time waiting for data.

Is there any reason you can't use asynchronous web fetching? I suspect there's no need to have a thread per task or even a thread per core here. TPL makes various aspects of asynchronous programming easier, with things like continuations.

In terms of efficiency, is your application actually CPU bound? It sounds like you need to be getting the maximum appropriate level of parallelism at the network side - that's the bit to concentrate on, unless the calculations are really heavyweight.

Jon Skeet
@Jon: I believe this was the true in the CTP release of the TPL. Now I think it is not the case.
andras
@andras: could you give more details about what you mean?
Jon Skeet
@Jon: I believe that the one thread/CPU case was true for the CTP release of the TPL (that you could use with VS2008). Now it uses the new CLR thread pool - which might or might not create more threads, depending on some heuristics.
andras
@Jon: it is true that you can specify the degree of parallelism for a task. You can however start multiple tasks and end up with more threads than cores. I am also not sure that the current or future TPL/thread pool implementation will not ramp up the number of threads if it detects that the CPU is underutilized.
andras
@andras: Yes, I strongly suspect it will.
Jon Skeet
Can you tell me what "asynchronous web fetching" is?I start a thread for each task which uses httpwebrequest to retrieve the data. The data is deserialized, the task performs the calculation and returns the result.The calculations are lightweight. The tasks are network bound, so it would make sense to have multiple threads to allow each to retrieve data simultaneously?
Bruce Adams
@Bruce: See things like WebClient.DownloadStringAsync. You don't need multiple *threads* to have multiple requests in flight at the same time.
Jon Skeet
Jon is right, of course - as always. :) Async fetching would be lighter on your machine than 30 blocking threads. +1
andras
(I suspect that it will make use of async I/O and I/O threads in the thread pool. Please correct me if I am wrong - I'm just guessing and curious. :)
andras
@Bruce: the thing that I'm not sure of and @Jon might shed some light on it - or you can test it for yourself - is the following: if I begin 30 async requests, will all of them start downloading immediately, or many of them may just sit there in the work queue of the thread pool waiting - and starting the download only when they are dequeued?
andras
@andras: Good point. I haven't had a chance to play with the code yet. But I’m very interested in the answer to this. It would be the deciding factor of whether to go with multithreading or asynchronous webrequests. What's your wisdom on this @Jon?
Bruce Adams
@andras: I believe they will all start immediately, so long as they're to different hosts. If you've got lots of requests to the same hosts, you'd need to increase the size of the connection pool to get more concurrency. I doubt that the choice between explicit threading and async requests will make much difference to performance - but obviously you'd want to test that.
Jon Skeet
A: 

Do you spawn 30 threads? Do you make use of a thread pool? I believe you the tpl will be much more optimized. Spawning threads is a rather expensive operation. I agree with Jon that tpl will usually use one thread per core. What .NET version are we talking here b.t.w.

Hannes de Jager
I do not make use of the threadpool.If the tasks are network bound, how can TPL be more optimised than multithreading?The comparison would be 30 simultaneous tasks vs 4 simultaneously task on quad core machine.
Bruce Adams
If the tasks are network bound then that makes my answer not applicable. Sorry my misunderstanding
Hannes de Jager
+3  A: 

As a general rule, there is nothing that stops the TPL to use more (or less) threads than cores.

To control the situation somewhat using TPL, my first approach would be: make sure that the threadpool max threads setting is at least 30, then parallelize the task with a maximum concurrency level of 30. Within the task, you can use a semaphore before you start the CPU-bound computation to limit concurrency to the number of cores. If you are not running under IIS or SQL server, you are able and may wish to set the minimum/maximum number of threadpool threads to 30 in order to prevent the thread pool heuristics playing with the number of threads too much. (Provided, of course, that TPL and the thread pool is not used for other purposes during this time in your application.)

The optimal number of threads depends on the situation. Consider e.g. your scenario: your tasks are not CPU bound when they retrieve data - they are network bound. As you start the tasks, it would be wise to increase parallelism so that downloads are carried out simultaneously. Your calculations may be CPU bound, however. In that case, decreasing the number of threads so that only one thread runs per core might yield better performance.

TPL is now based on the new CLR Thread Pool.
The thread pool uses heuristics to decide about the number of threads.
There is a Channel9 video about the new thread pool with some insight.
The heuristics of the old thread pool and some bits about the new can be found here (last paragraph "What the Future Holds?").

The algorithm and the numbers were subject to changes throughout the different versions of the CLR.
It might be the case in the future as well.

There are many posts about the concurrency level, one I came across is here.

andras
Given that these are network bound tasks. Would creating and starting 30 new threads not perform better than letting TPL handle the threading?
Bruce Adams
You can do that as well - start 30 of them and then make sure that the calculation only runs on as many cores as you have with a semaphore. Be aware that if you start 30 downloads to the same IP from one client, you are not following the HTTP spec recommendation. :)
andras
@Bruce: threading might perform better in your case. The difference would be marginal though, I suspect.
andras
+1 for the info and help on understanding TPL a little more. Thanks
Bruce Adams
A: 

I have an application which performs 30 independent tasks simultaneously using multithreading, each task retrieves data over http, performs a calculation and returns a result to the ui thread.

That is an IO-bound concurrent program.

Can I use TPL to perform the same tasks?

You can but the TPL is designed for CPU-bound parallel programs so you would be abusing it.

Does TPL create 30 new threads and spread them over all the available cores, or does it just split the tasks over the available cores and use one thread per core?

Neither. The TPL essentially uses per-core wait-free work-stealing task queues to dynamically load balance CPU-intensive computations as they run.

Will there be a performance boost using TPL over multithreading in this case?

You will save 30 thread creations and the extra contention your unnecessary threads incur.

The correct solution to your problem is to write an asynchronous program that does not block threads. This is done by expressing the remainder of your computation after your downloads are complete as a continuation that is invoked with the data when the download has completed.

Microsoft's new F# programming language includes features specifically designed to make this easy. For example, your problem can be solved with only 5 lines of code in F#:

let fetchCalcAndPost uris calc post =
  for uri in uris do
    async { use client = new System.Net.WebClient()
            let! data = client.AsyncDownloadString uri
            do calc data |> post }
    |> Async.Start

This solution never blocks any thread so it is fully concurrent.

Jon Harrop