Android thread runnable performance
Asked Answered
S

1

5

I'm wondering about performance and cpu/ram requirements for 2 different methods of starting runnables

I have some code that collects sensor data every 10ms and inserts the values into a database on a background thread (using a single thread executor). Executor service is created as follows:

executor = Executors.newSingleThreadExecutor();

One way to do that would be something like...

public void onSensorChanged(SensorEvent event) {
    //get sensor values

    //insert into database
    executor.execute(new Runnable(){
        //database insert code here
    });
}

I see this method a lot in tutorials, but because I'm doing this every 10ms, it feels resource intensive as I'm creating a new object every single time a sensor value change is detected. Does this new object just get overridden every 10ms? Or is it taking up increasing amounts of RAM as new objects are created?

I have since refactored my code to look more like this:

executor = Executors.newSingleThreadExecutor();
myRunnable = new MyRunnable();

class MyRunnable implements Runnable {
    public void run() {
        //database insert code here
    }
}

public void onSensorChanged(SensorEvent event) {
    //get sensor values

    //insert into database
    executor.execute(myRunnable);
}

My thinking is that I instantiate only a single object once, instead of doing it every time sensors change. Am I correct in thinking this has lower RAM usage than the previous method? Is there a more efficient/better way to accomplish this task?

Scissile answered 4/4, 2016 at 5:57 Comment(12)
why can't you just create a queue to exchange data btw sensor and db writing thread? Thus you could create only one runnable for writer thread, which in infinite loop takes data from the queue and saves them.Degreeday
I thought in my second example I am only creating 1 runnable for the background thread? Maybe I'm misunderstanding what you mean, but I thought that executor.execute() is adding tasks to a queue, where each queued task is doing the save?Scissile
Yes you understand correctly. Your second example will work I suppose, if you synchronize shared data correctly (I mean the code which you omitted, where events from the sensor are stored in some object, then read by writer).Degreeday
in first example, you took data that need to be saved directly in Runnable. In second example you cannot, because event is not visible in MyRunnable. So you will need some another intermediate object where SensorEvent objects (or derivative) are stored prior save to DB. I suggested queue as the most common pattern for producer-consumer task.Degreeday
well the consumer-producer queue might be the most efficient way , and also you can use AsyncTask which is already provided by the Android. In the example that you are using the same instance of the runnable , i suppose that you are passing some details in it before commiting to the executorService queue. What will happen in case that you have a second onSensorChanged() call , but the previous one havent finished yet ? if you make the fields volatile , it wont be async , and if you dont , you will lose some callsObcordate
Interesting. Well, in my second method, I have a float array that starts off empty. During onSensorChanged I collect the sensor values as an array and replace that initial float array. So that array gets overwritten every 10ms with the latest values. The MyRunnable class then just gets the values of that array and inserts into the databaseScissile
The sensors will probably change very often so I'll likely get a second event before the previous one gets committed. But I think a single threaded executor takes care of that problem. Doesnt executor.execute() just add things to a queue? So even if a second set of sensor data comes in, it will just sit in the queue behind the first set and it'll all get processed in the order of arrival. Thats what I'm hoping happens anyway, otherwise you're right, I'll lose some dataScissile
@Scissile thus, you create a new array every 10 ms instead of creating a new Runnable. So what is the difference (except more sophisticated design)? =)Degreeday
Thats a good point, thanks for pointing it out. I guess I'm hoping that the float array will always take up a fixed amount of space because I'm not appending to it, I'm always just replacing it completely with the same number of values. Whereas with the new Runnable method I'm not sure if that 1) just keeps overwriting the same object in RAM, or 2) keeps allocating more and more memory with every new object? This is the type of thing I wanted to clear upScissile
@Scissile if you use a single array to store data, you should synchronize access to it--to make sure a writer always sees consistent values. Answering your question regarding memory consumption: objects that become unreferenced are very quickly cleared by GC, and memory becomes available--so you should not care about it at all, unless you deal with large objects that should be visible for a long time, i. e. images, bitmaps, shared large data etc (not your case).Degreeday
Does synchronized access matter if one thread is only ever writing to the array, and the other thread is only ever reading (and inserting to db)?Scissile
I've started a new thread about the synchronized problem (#36400783) so this one doesn't get too far off topic. Thanks for your input so far!Scissile
S
2

Creating new Runnable instance every 10 ms will surely cause garbage collector to kick in more often and may impact your application performance. Your second approach i think is much better.

Suisse answered 4/4, 2016 at 6:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.