在多处理器线程中优化内存数据库的QSql

时间:2012-01-16 08:43:20

标签: multithreading qt sqlite in-memory-database

这是我的问题,我使用sqlite有一个memory database QSql。我有几个线程,每个线程处理这个公共数据库的一个不同的表。我使用Win API来确保这些线程在不同的处理器上运行,如下所示:

SetThreadAffinityMask (hThread, processorMask);

当只有一个线程处理一个表时,它需要10秒并占用总CPU的25%。但是当有4个线程处理4个不同的表时,它需要将近40秒并且仅占总CPU的35%。我认为原因是在一个数据库中存在某种thread-safe同步。但是由于不同的线程读取或写入不同的表,线程安全会减慢我的程序。我该如何优化它。

更新:最可能的原因是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();
}

3 个答案:

答案 0 :(得分:1)

首先,不要显式设置affinity mask,windows会自动在最空闲的内核上分配线程。在这种情况下,最好依靠操作系统来进行线程分发。

据我所知,sqlite在写入时会锁定整个数据库,这就是为什么你没有得到预期的性能提升。看一下sqlite锁定文档http://www.sqlite.org/lockingv3.html

答案 1 :(得分:0)

您是否测量过线程在CPU上花费的时间与磁盘I / O吞吐量相比?

这可能与线程和锁无关。它可能与Amdahl's law有关。

答案 2 :(得分:0)

Qt文档对此毫不含糊。来自http://doc.qt.nokia.com/4.7/threads-modules.html#threads-and-the-sql-module

  

线程和SQL模块

     

只能在创建连接的线程中使用连接。   在线程之间移动连接或从中创建查询   不支持不同的线程。

     

此外,QSqlDrivers使用的第三方库也可以   在多线程中使用SQL模块的进一步限制   程序。有关更多信息,请参阅数据库客户端手册   信息

没有办法通过Qt API做你想做的事。