Symbian multithreading testing

    We recently installed the SDK for development under Qt for Symbian on Linux. Now it's time to write something on it.
    Now almost everywhere multithreaded architectures are used to perform any background calculations while the user uses the UI.
    Let's see how effective this is when developing for Symbian.

    For tests, the Nokia 6120c phone (S60 v3 FP1) was taken. At one time, this phone was popular as a budget and small smartphone and is a very typical device on the Symbian platform.

    Test Description


    As background calculations, an algorithm for checking numbers for simplicity was taken. It is quite simple and short and at the same time consumes a lot of processor time.
    bool isPrime = true;
    for (int i = 2; i <= currentDummy/2; isPrime &= (currentDummy%i) != 0, ++i);

    * This source code was highlighted with Source Code Highlighter.


    For the purity of the experiment, we run through all the numbers, and do not stop at the first divider found (otherwise the flows would end with a very large time spread). The data between the streams is divided evenly and not by segments of the number line, but alternate between the streams (which again gives us a more honest experiment with almost simultaneously ending streams).

    Stream class code (the final one is laid out and includes two options at once, more on that below).
    //workerthread.h
    #ifndef WORKERTHREAD_H
    #define WORKERTHREAD_H

    #include

    class WorkerThread : public QThread
    {
    Q_OBJECT
    public:
      explicit WorkerThread(int start, int end, int step, QObject *parent = 0);

    signals:
      void updateProcess(int value);

    public slots:

    protected:
      void run();

    private:
      int rangeStart;
      int rangeEnd;
      int rangeStep;
    };

    #endif // WORKERTHREAD_H

    //workerthread.cpp
    #include "workerthread.h"

    WorkerThread::WorkerThread(int start, int end, int step, QObject *parent) :
      QThread(parent), rangeStart(start), rangeEnd(end), rangeStep(step)
    {
    }

    void WorkerThread::run()
    {
      int currentDummy = rangeStart;
      int currentProcess = 0;
      while (currentDummy <= rangeEnd)
      {
        bool isPrime = true;
        for (int i = 2; i <= currentDummy/2; isPrime &= (currentDummy%i) != 0, ++i)
        {
          //Method 2. Slower, but more responsive UI
    //      if (!(i%500))
    //        yieldCurrentThread();
        }
        if (!(currentProcess%1000))
        {
          emit updateProcess(currentProcess);
        }
        currentDummy += rangeStep;
        ++currentProcess;

        //Method 1. Faster, but with UI stucks
        yieldCurrentThread();
      }
      emit updateProcess(currentProcess);
    }

    * This source code was highlighted with Source Code Highlighter.


    To study the UI brakes, we took a regular slider, which changed its value according to the timer in the GUI thread. Thus, the moments when the main thread does not receive control (which leads to UI braking and freezing) we can track by this slider.
    Here is the appearance of the testing form:
    image
    It is such a minimalist style, only the percentage of calculations (progress bars), the time of calculations, the UI indicator, the number of threads and the Start button are displayed.

    Testing


    As mentioned earlier, we have two options for implementing the workflow:
    1. Give ( yield ) control to another thread after each checked number
    2. Give ( yield ) control to another thread after a certain number of checked dividers

    Even without tests, it is clear that the first option will work faster, but sometimes slow down the UI, and the second will work slower (due to more context switches), but also slow down the UI less. The whole question is how much this will be critical and noticeable.
    We will test for 1, 2, 3, and 4 workflows, evaluating the operating time and smoothness of the UI (that is, the smoothness of the slider).

    Testing the first option


    1 thread

    It was executed in 64255ms, while there were about a dozen slowdowns (time about a second), but in general the slider moved smoothly.

    2 threads

    Completed in 49477ms, while there were 6 brakes (about a second time). General feeling as from a single-stream option.

    3 threads

    Completed for 45988ms, while there was only one slowdown, but it was from the middle of the calculations to the end. The result is far from satisfactory in terms of UI.

    4 threads

    Completed in 46275ms, while working similarly to the option with 3 threads.

    Conclusions on the first option

    It is undesirable to run more than two worker threads, but two threads give a speed increase of about 25 percent and practically do not affect the UI.

    Testing the second option


    1 thread

    It was completed in 463372ms, while there were a dozen slowdowns (time about a second) and a lot of small ones (much less than a second, but noticeable to the eye).

    2 threads

    Completed in 231544ms, while there were 6 brakes (about a second time) and, like in the version with one stream, there are a lot of small ones.

    3 threads

    Completed in 154797ms, while there were about 5 slowdowns (also about a second), but in general the UI worked smoothly.

    4 threads

    Completed for 116959ms, while working similarly to the option with 3 threads.

    Conclusions on the second option

    Naturally, the test with one thread in this option is needed just for comparison, since such an aggressive yellowing does not give any advantages for a single thread. The two-thread test also showed not very good results due to too much overhead for the scheduler. Tests with three and four threads showed themselves very well.

    General conclusions


    In the case when we need to divide some kind of homogeneous computation into threads, it is better to use the first option with two work flows of course. But in the case when we need to perform some heterogeneous operations in different threads, the second option looks better than the first (although it takes two to three times as much time), since it allows the user to work normally with the UI during calculations.

    My findings


    I am glad for such results, since they clearly show that even for budget (and already quite old) Symbian devices, you can write multithreaded applications without any problems, albeit with a few caveats and additional tests (for example, at the optimal frequency of yelding).

    Download the above


    Sources
    sis file of the first variant
    sis file of the second variant

    UPD about thread priorities


    Launched threads with Idle and Low priorities (only the first test option was launched). The results were even worse than with Normal priority. Two streams froze in the middle, three and four streams froze almost at the very beginning. The speed differs within the margin of error (48s for two and 45 for three and four). Apparently, the sheduler does not work very well with different thread priorities.


    Also popular now: