MultiProcessor-ThreadのメモリデータベースのQSQLを最適化します
-
28-10-2019 - |
質問
これが私の問題です、私はそれを持っています sqlite
memory database
使用して QSql
. 。この一般的なデータベースの1つの異なるテーブルを処理する各スレッドがあります。そして私は使用します Win API
このようなさまざまなプロセッサでこれらのスレッドが動作することを確認するには:
SetThreadAffinityMask (hThread, processorMask);
1つのテーブルを処理するスレッドのみがある場合、10秒かかり、総CPUの25%を使用します。ただし、4つの異なるテーブルを処理する4つのスレッドがある場合、40秒近くかかり、CPUの35%しか使用しません。その理由は、ある種のものがあるからだと思います thread-safe
1つのデータベースで同期します。しかし、異なるスレッドの読み取りや異なるテーブルの書き込みにより、スレッドセーフは私のプログラムを遅くします。どうすれば最適化できますか。
アップデート: 最も可能な理由は、内部のある種のロックです Qt
または Sqlite 3
私のプログラムを遅くするので、事前設定によってこれらのロックをシャットダウンまたはバイパスすることは可能です。
update2: これが例です。 (多分少し長い、ごめんなさい)
class MultiProcessorThread
{
public:
virtual void run();
bool start()
{
m_hThread = CreateThread (NULL, 0, MultiProcessorThread::ThreadFunc, this, CREATE_SUSPENDED, NULL);
if (m_hThread != INVALID_HANDLE_VALUE)
{
RunningThreadCount++;
m_ProcessorMask = 1 << ( (RunningThreadCount - 1) % ProcessorCount);
SetThreadAffinityMask (m_hThread, m_ProcessorMask); // Make thread working on different processor
ResumeThread (m_hThread);
return true;
}
else
return false;
}
protected:
static DWORD WINAPI ThreadFunc (LPVOID in);
HANDLE m_hThread;
DWORD_PTR m_ProcessorMask;
static DWORD_PTR ProcessorCount;
static DWORD_PTR RunningThreadCount;
static DWORD_PTR GetNumCPUs();
};
DWORD_PTR MultiProcessorThread::ProcessorCount = GetNumCPUs();
DWORD_PTR MultiProcessorThread::RunningThreadCount = 0;
DWORD_PTR MultiProcessorThread::GetNumCPUs() // Get how many processors on this PC
{
SYSTEM_INFO m_si = {0};
GetSystemInfo (&m_si);
return (DWORD_PTR) m_si.dwNumberOfProcessors;
}
DWORD WINAPI MultiProcessorThread::ThreadFunc (LPVOID in)
{
static_cast<MultiProcessorThread*> (in)->run();
return 0;
}
class Run : public MultiProcessorThread
{
public:
void run()
{
int i = 0;
QString add = "insert into %1 values(1)";
add = add.arg (table);
QString sel = "select a from %1 ";
sel = sel.arg (table);
QString del = "delete from %1 where a=1";
del = del.arg (table);
while (++i) // read and write database
{
query.exec (add);
query.exec (sel);
query.exec (del);
}
}
QSqlQuery query;
QString table;
};
int main (int argc, char *argv[])
{
QCoreApplication a (argc, argv);
QSqlDatabase db = QSqlDatabase::addDatabase ("QSQLITE", "test");
db.setDatabaseName (":memory:"); // All threads working on the same memory database.
db.open();
QSqlQuery q (db), q1 (db), q2 (db);
q.exec ("create table A (a)");
q1.exec ("create table B (a)");
q2.exec ("create table C (a)"); // All threads working on different table.
Run b[3];
b[0].query = QSqlQuery (q);
b[0].table = "A";
b[1].query = QSqlQuery (q1);
b[1].table = "B";
b[2].query = QSqlQuery (q2);
b[2].table = "C";
b[0].start();
b[1].start();
b[2].start();
return a.exec();
}
解決
まず第一に、アフィニティマスクを明示的に設定しないでください。Windowsは、最もアイドル状態のコアにスレッドを自動的に割り当てます。この場合、コードよりもスレッド分布を行うためにOSに依存する方が良いです。
私の知る限り、sqliteは書いている間にデータベース全体をロックします。そのため、予想したパフォーマンスが得られません。 SQLiteロックドキュメントをご覧ください http://www.sqlite.org/lockingv3.html
他のヒント
たとえば、ディスクI/Oスループットと比較して、スレッドがCPUにどれだけの時間を費やしているかを測定しましたか?
これは、スレッドとロックとは何の関係もありません。それはすべてと関係することができます アムダールの法律.
QTドキュメントはこれについて明確です。から http://doc.qt.nokia.com/4.7/threads-modules.html#threads-and-the-sql-module:
スレッドとSQLモジュール
接続は、それを作成したスレッド内からのみ使用できます。スレッド間の接続を移動したり、別のスレッドからクエリを作成したりすることはサポートされていません。
さらに、QSQLDRIVERSが使用するサードパーティライブラリは、マルチスレッドプログラムでSQLモジュールを使用することにさらなる制限を課すことができます。詳細については、データベースクライアントのマニュアルを参照してください
QT APIを通じてやりたいことをする方法はありません。