Asynctask, Must It Take Such a Performance Penalty Hit...

AsyncTask, must it take such a performance penalty hit...?

You're not the only one observing this behaviour. The slowdown by factor 10 is probably a result of Android using a Linux cgroup (scheduling class) for threads of priority BACKGROUND or below. All these threads have to live with 10% CPU time altogether.

The good news is you don't have to live with the Thread priority settings from java.lang.Thread. You can assign your Thread a pthread (Linux thread) priority from the definitions in android.os.Process. There, you not only have Process.THREAD_PRIORITY_BACKGROUND, but also constants to adjust the priority a bit.

Currently, Android uses the background thread cgroup for all threads with priority THREAD_PRIORITY_BACKGROUND or worse, and THREAD_PRIORITY_BACKGROUND is 10 while THREAD_PRIORITY_DEFAULT is 0 and THREAD_PRIORITY_FOREGROUND is -2.

If you go for THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE (aka 9) your thread will be lifted out of the background cgroup with the 10% limitation, while not being important enough to interrupt your User Interface threads too often.

I believe there are background tasks which need a bit of computational power but which are at the same time not important enough to de facto block the UI (by consuming too much CPU in a separate thread) and Android currently has no obvious priority to assign to these, so in my view, this is one of the best priorities you can assign to such a task.

If you can use a HandlerThread it's easy to achieve:

ht = new HandlerThread("thread name", THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE);
ht.start();
h = new Handler(ht.getLooper());

If you want to go with AsyncTask, you can still do

protected final YourResult doInBackground(YourInputs... yis) {
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE);
...
}

but be aware that the underlying implementation may reuse the same Thread object for different tasks, for the next AsyncTask, or whatever. It seems that Android simply resets the priority after doInBackground() returns, though.

Of course, if your UI really consumes CPU and you want more power for your task at the same time, taking it away from the UI, you can set another priority, maybe up to Process.THREAD_PRIORITY_FOREGROUND.

Running off of UIthread without AsyncTask

Since you already got the AsyncTask working, maybe you could just bump it's priority from inside doInBackground():

protected Void doInBackground() {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
/* do the work */
}

Also, for the Handler, not exactly sure what the problem was, but you need to create the Handler in the UI thread, explicitly start a different thread to do your work, then use the handler from that new thread to post messages to the UI thread. AsyncTask is nice because it takes care of all this for you.

Android why update UI in handler or asynctask when you are already on

I find it a bit hard to tell what exactly your question is, but a few notes regarding your thesis.

No, #1 to #4 are not the same.

  • Validity: Yes, if you are on the UI thread already and have no expensive computations or lengthy I/O operations to do, then there's no need to choose from #2 to #4.
  • Timing: Depending on circumstances, the effects of #2 to #4 have a certain probability of appearing a bit later than #1.
  • Complexity/waiting: Only #4 will in general enable you to cleanly wait for blocking I/O or do a lengthy calculation. Depending on the Handler, #2 may also work, but that's not what Handlers are made for.
  • Handlers: In your #2, there is no guarantee that you are using the main/UI thread handler at all. The code could well run on a non-main/UI thread.
  • 3 is a convenience method.

So no, they are far from being the same in general.

P.S.

  • Priority: Depending on what kind of Thread your code ends up when you use a Handler, and in any default case for doInBackground() of AsyncTask, your code will run with background priority and have to live with 10% CPU time together with all other background threads on the system unless you adjust the priority. For a detailed discussion, see here.

P.P.S.

Since you asked "the other way round" in a comment:

  • 1 is the most effective and direct if you are sure you're on the UI thread and don't do calculations and/or I/O

  • 2 is only valid if the handler uses the main/UI thread. WHy use it? If you're somewhere in your code, detached from any available Context or View, you can still do newHandler(Looper.getMainLooper()) and that gives you a Handler for the UI thread.

  • 3 refers to View and Context related convenience methods. As soon as you have one of those objects at hand, you can use them to do something on the UI thread.

  • 4 must be used if you're doing expensive operations or waiting. However, AsyncTask is only a convenience class for this purpose and intended for relatively short operations.

async await performance?

A Task object represent the deferred result of a pending operation. You don't have to use tasks and async/await if you don't have any pending operations. Otherwise, I believe async/await code is generally more efficient than its bare TPL ContinueWith analogue.

Let's do some timing:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
class Program
{
// async/await version
static async Task<int> Test1Async(Task<int> task)
{
return await task;
}

// TPL version
static Task<int> Test2Async(Task<int> task)
{
return task.ContinueWith(
t => t.Result,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}

static void Tester(string name, Func<Task<int>, Task<int>> func)
{
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i < 10000000; i++)
{
func(Task.FromResult(0)).Wait();
}
sw.Stop();
Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
}

static void Main(string[] args)
{
Tester("Test1Async", Test1Async);
Tester("Test2Async", Test2Async);
}
}
}

The output:


Test1Async: 1582ms
Test2Async: 4975ms

So, by default, await continuations are handled more efficiently than ContinueWith continuations. Let's optimize this code slightly:

// async/await version
static async Task<int> Test1Async(Task<int> task)
{
if (task.IsCompleted)
return task.Result;
return await task;
}

// TPL version
static Task<int> Test2Async(Task<int> task)
{
if (task.IsCompleted)
return Task.FromResult(task.Result);

return task.ContinueWith(
t => t.Result,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}

The output:


Test1Async: 1557ms
Test2Async: 429ms

Now the non-async version wins. In case with the async version, I believe this optimization has already been done internally by the async/await infrastructure.

Anyway, so far we've dealt only with completed tasks (Task.FromResult). Let's introduce the actual asynchrony (naturally, we'll do less iterations this time):

static Task<int> DoAsync()
{
var tcs = new TaskCompletionSource<int>();
ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(0));
return tcs.Task;
}

static void Tester(string name, Func<Task<int>, Task<int>> func)
{
ThreadPool.SetMinThreads(200, 200);
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i < 1000000; i++)
{
func(DoAsync()).Wait();
}
sw.Stop();
Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
}

The output:


Test1Async: 4207ms
Test2Async: 4734ms

Now the difference is very marginal, although the async version still performs slightly better. Yet I think such gain is really neglectable, comparable to the actual cost of the asynchronous operation or to the cost of restoring the captured context for when SynchronizationContext.Current != null.

The bottom line is, if you deal with asynchronous tasks, go for async/await if you have a choice, not for performance reason but for ease of use, readability and maintainability.

Changing the image of the ImageButton when the onClick is called

Your problem is that you sleep() on (or otherwise block) the UI thread. Please understand that the main/UI thread is responsible for redraws and all event handling. If you sleep there (onClick() is executed ion the UI thread), then Android will be unable to do anything user interface related.

The background is that a multithreaded GUI would be extremely inefficient. Most UI related calls, such as setBackground, simply set some data or even schedule some more activities, have no immediate effect and will only yield results if the control returns to the main UI thread control loop, e.g. for a coordinated redraw. Just imagine what would happen if you launched a couple of layout changes one after another and each one would be processed immediately -- that would result in a huge number of computations which would become invalid right the next moment.

So the main thread is nothing but a huge loop processing all kinds of callbacks, including Activity lifecycle methods, event listener callbacks etc. So the UI is single threaded if you will.

What you need to do is perform your long-running operation on an AsyncTask or Thread. For starters, I'd try an AsyncTask. In the AsyncTask's onPostExecute() method, which is run on the UI thread again, you can manipulate your user interface as you wish.

For a quick success, you can also use a Thread:

@Override
public void onClick(View v) {
// on UI thread
btnTest.setImageResource(R.drawable.add);
// launch concurrent thread
(new Thread(new Runnable() {
@Override
// the code of this method will run concurrently
public void run() {
System.out.println("start");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("end");
// at this point, we need to get back to the UI thread
// for simplicity, we use the view's convenience method for that
v.post(new Runnable() {
@Override
// the code of this method will run on the UI thread
public void run() {
btnTest.setImageResource(R.drawable.del);
}
})
}
})
).start();
}

AsyncTask looks much nicer but it's not easily created and used on-the-fly.

Please note that the code above creates a Thread which will run until either it leaves its run() method, or Android kills the Linux process which hosts your app.

Also note that this Thread holds references to a lot of objects from your code, because it can access everything from its surrounding scope.

The conclusion is, that if your Thread does run for a long time, it could hold references to a View which is not even visible anymore, so zombie memory objects could worsen the resource consumption of your app.

Also, you mentioned that you're doing a long-running operation there. The question is, does that operation wait or does it compute something. If it computes something, you may find that it now takes ten times longer unless you adjust the AsyncTask/Thread priority as described here.



Related Topics



Leave a reply



Submit