Bug 26548 - Regression: ThreadPool.QueueUserWorkItem is much slower than before
Summary: Regression: ThreadPool.QueueUserWorkItem is much slower than before
Alias: None
Product: Runtime
Classification: Mono
Component: General ()
Version: unspecified
Hardware: Other Other
: --- major
Target Milestone: ---
Assignee: Ludovic Henry
Depends on:
Reported: 2015-01-29 12:29 UTC by Mathieu Miller
Modified: 2016-01-25 17:41 UTC (History)
7 users (show)

Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Mathieu Miller 2015-01-29 12:29:35 UTC
Calling ThreadPool.QueueUserWorkItem is much slower in 8.6 than 8.4.

Using Xamarin.iOS

 - 29 calls, average 0.669s, total 19.43s

Using version

 - 29 calls, average 4.1s, total 1:59.9

These tests were ran on an iPhone6+ with iOS 8.1.2

I'm using monotouch.dll NOT the Xamarin.ios assembly.
Comment 1 Sebastien Pouliot 2015-01-29 13:11:15 UTC
`ThreadPool.QueueUserWorkItem ` is part of corlib and is not directly related to XI. The performance regression might comes from changes in Mono 3.10 (XI 8.4) and 3.12 (8.6).

OTOH it's a bit strange that 29 calls took so long - even in 8.4 so it could be related to what's being done in the threads.

Can you share your test case with us so we can profile where the exact time is being spent ?
Comment 2 Mathieu Miller 2015-01-29 13:42:16 UTC
The time reported should be only the time it took to get the thread going.

Here is the snippet of code I used. I made sure all the calls to threadpool in my app were going to this instrumented version instead:

public class ThreadPoolExt
    private static long _queueCallCount;
    private static long  _queueTotalTime;

    // Queue an async action
    public static bool Queue(Action action)
        Stopwatch st = new Stopwatch();
        return ThreadPool.QueueUserWorkItem((s) =>
              long elapsed = st.ElapsedTicks;
              Interlocked.Increment(ref _queueCallCount);
              Interlocked.Add(ref _queueTotalTime, elapsed);

          }, null);

    public static void Report()
        if (_queueCallCount > 0)
            TimeSpan total = new TimeSpan(_queueTotalTime);
            TimeSpan average = new TimeSpan(_queueTotalTime/_queueCallCount);

            Console.Write(string.Format({0} calls, average {1}, total {2}", _queueCallCount, average, total));
Comment 3 Sebastien Pouliot 2015-01-29 14:03:08 UTC
That's not classes that XI customize. Are you aware of any changes between Mono 3.10 (XI 8.4) and Mono 3.12 (XI 8.6) that could results in such slowdowns ?
Comment 4 Mathieu Miller 2015-01-29 14:18:35 UTC
As a Xamarin end-user, I don't see changelogs from mono when I update the app. To me, it's all the same product :)

Where should I report this bug?
Comment 5 Sebastien Pouliot 2015-01-29 14:21:35 UTC
@Mathieu what you did is perfectly fine :)

I assigned someone to the issue and my question from comment #3 was addressed to him. I just forgot to prefix my comment with his name (like I just did for you :-)
Comment 6 Rodrigo Kumpera 2015-01-30 13:20:00 UTC
Hi Mathieu,

Could you explain what your code does in action ()?

We changed thread creation heuristics to be less aggressive as it was frequently thrashing the system.
Comment 7 Mathieu Miller 2015-01-30 13:55:34 UTC
mostly tasks that can take from 1 to 10 seconds to complete. There are some that never exit though, mostly waiting for some tasks themselves. I'm changing those so that they create their own threads instead of allocating it through the pool.
Comment 8 Rodrigo Kumpera 2015-01-30 14:21:26 UTC
The recommended way to execute long running tasks in the threadpool is with:

Task.Factory.StartNew (myAction, TaskCreationOptions.LongRunning)

This will get you a task object but will have the long running code in a thread that doesn't
interfere with the threadpool regular operations.
Comment 9 Jeremy Kolb 2015-05-26 16:44:41 UTC
Is this related to http://forums.xamarin.com/discussion/comment/124322/#Comment_124322 ? I'm seeing the same thing where tasks sometimes take up to 7 or 8 seconds to start in Release mode (but usually not debug).
Comment 10 Ludovic Henry 2016-01-25 17:41:05 UTC
The threadpool implementation has changed since Mono 3.12, as it now uses the Microsoft implementation with Mono 4.2

@Mathieu if you can confirm this behavior with XI 9.2+, please reopen the bug. Thank you!