現在、マルチスレッドアーキテクチャは、ユーザーがUIを使用している間、バックグラウンド計算を実行するためにほぼどこでも使用されています。
Symbian用に開発するとき、これがどれほど効果的か見てみましょう。
テストでは、Nokia 6120c電話(S60 v3 FP1)が使用されました。 かつて、この電話は予算の小さいスマートフォンとして人気があり、Symbianプラットフォームで非常に典型的なデバイスでした。
テストの説明
バックグラウンド計算として、簡単にするために数字をチェックするアルゴリズムが採用されました。 これは非常に単純で短く、同時に多くのプロセッサー時間を消費します。
bool isPrime = true ;
for ( int i = 2; i <= currentDummy/2; isPrime &= (currentDummy%i) != 0, ++i);
* This source code was highlighted with Source Code Highlighter .
実験の純度のために、すべての数値を調べ、見つかった最初の仕切りで停止しません(そうしないと、フローは非常に長い時間の広がりで終了します)。 ストリーム間のデータは、番号線のセグメントではなく、均等に分割されますが、ストリーム間で交互になります(これにより、ほぼ同時に終了するストリームでより正直な実験ができます)。
ストリームクラスコード(最後のコードを配置し、2つのオプションを同時に含みます。詳細は以下を参照してください)。
//workerthread.h
#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H
#include <QThread>
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 .
UIブレーキを研究するために、GUIスレッドのタイマーに従って値を変更する通常のスライダーを使用しました。 したがって、メインスレッドが制御を受け取らない瞬間(UIのブレーキとフリーズにつながる)は、このスライダーで追跡できます。
テストフォームの外観は次のとおりです。
シンプルなスタイルで、計算の割合(進行状況バー)、計算の完了時間、UIインジケーター、スレッド数、および[開始]ボタンのみが表示されます。
テスト中
前述のように、ワークフローを実装するための2つのオプションがあります。
- 各チェック番号の後に別のスレッドに制御を与える( yield )
- 一定数のチェックされたディバイダーの後に別のスレッドに制御を与える( yield )
テストを行わなくても、最初のオプションはより速く動作しますが、UIが遅くなることがあり、2番目のオプションはより多く動作します(コンテキストスイッチが多いため)が、UIの速度も遅くなります。 全体の問題は、これがどれほど重要で目立つかということです。
1、2、3、および4のワークフローをテストし、操作時間とUIの滑らかさ(スライダーを動かす滑らかさ)を評価します。
最初のオプションのテスト
1スレッド
約12のスローダウン(約1秒)がありましたが、64255msで完了しましたが、一般的にスライダーはスムーズに動きました。
2スレッド
49477msで完了し、6つのブレーキがありました(約2回目)。 シングルストリームオプションとしての一般的な感覚。
3スレッド
スローダウンは1つしかありませんでしたが、45988ミリ秒で完了しましたが、計算の途中から最後まででした。 結果は、UIの観点からは満足できるものではありません。
4スレッド
46275msで完了しましたが、3スレッドのオプションと同様に機能しました。
最初のオプションに関する結論
3つ以上のワーカースレッドを実行することは望ましくありませんが、2つのスレッドは約25%の速度向上をもたらし、実際にはUIに影響しません。
2番目のオプションのテスト
1スレッド
463372msで完了しましたが、12のスローダウン(約1秒の時間)と多くの小さなスローダウン(1秒よりはるかに短いが、目には見えます)がありました。
2スレッド
231544msで完了し、6つのブレーキ(約2回目)があり、1つのストリームのバージョンのように、多くの小さなブレーキがあります。
3スレッド
154797msで完了しましたが、約5つのスローダウン(約1秒)がありましたが、一般的にUIはスムーズに機能しました。
4スレッド
116959msで完了しましたが、3つのスレッドを使用するオプションと同様に機能します。
2番目のオプションに関する結論
当然、このオプションで1つのスレッドを使用したテストは、比較のためだけに必要です。これは、このような積極的な黄変は、単一のスレッドに利点をもたらさないためです。 また、2スレッドテストは、スケジューラのオーバーヘッドが大きすぎるため、あまり良い結果を示しませんでした。 3つと4つのスレッドを使用したテストは、非常によくわかりました。
一般的な結論
ある種の同種の計算をスレッドに分割する必要がある場合は、もちろん2つのワークストリームで最初のオプションを使用することをお勧めします。 ただし、異なるスレッドで異種操作を実行する必要がある場合、2番目のオプションは最初のオプションよりも見栄えがよくなります(ただし、2〜3倍の時間がかかります)。
私の調査結果
このような結果に満足しています。予算(および既にかなり古い)Symbianデバイスであっても、いくつかの警告と追加のテスト(たとえば、最適な頻度で)を行っても、問題なくマルチスレッドアプリケーションを作成できることを明確に示しているためです。
上記をダウンロード
ソースコード
最初のオプションのsisファイル
2番目のオプションのsisファイル
スレッドの優先順位に関するUPD
アイドルおよび低優先度のスレッドを起動しました(最初のテストバージョンのみが起動されました)。 結果は、通常の優先度よりもさらに悪化しました。 途中で2つの流れが凍結し、3つと4つの流れがほぼ最初に凍結しました。 速度は誤差の範囲内で異なります(2の場合は48秒、3および4の場合は45秒)。 どうやら、シェダーは異なるスレッドの優先順位ではあまりうまく機能しません。