【问题标题】:Optimize QSql of memory database in multiprocessor-thread在多处理器线程中优化内存数据库的QSql
【发布时间】:2012-01-27 22:17:58
【问题描述】:

这是我的问题,我有一个 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();
}

【问题讨论】:

  • 这真的取决于你如何使用 sqlite3。粘贴一些您正在对数据库执行的操作的示例。
  • 在进行优化之前,请先看看这个问题:stackoverflow.com/questions/1680249/…
  • @Neox 它有一些帮助,但我的情况有一些不同。首先,在我的程序中我直接使用QSql 而不是sqlite,所以我无法选择如何编译sqlite3 lib。其次,我不需要在线程之间同步,因为它们在不同的表上运行。第三,我使用内存数据库,所以我必须共享相同的连接。
  • @michael911009 SQLite 支持多线程,但是对同一个 db 文件的写访问仍然是序列化的。 sqlite.org/faq.html#q5 在我看来,SQLite 的多线程是不值得的。
  • @Neox 这个怎么样?我创建了几个内存临时数据库,每个线程使用一个数据库,在各种插入操作之后(由于计算需要很长时间),我将所有临时数据库的数据附加到一个数据库。

标签: multithreading qt sqlite in-memory-database


【解决方案1】:

首先,不要显式设置关联掩码,windows 会自动在最空闲的核心上分配线程。在这种情况下,依赖操作系统进行线程分发比你的代码更好。

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 根本没有磁盘I/O,纯内存数据库计算。
    • 好的,不是磁盘,但这不是重点。它可能是导致阿姆达尔效应的任何阻塞资源。
    【解决方案3】:

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

    线程和 SQL 模块

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

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

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

    【讨论】:

    • 但是,我可以做到(使用 QSql btwn 线程),因为我的代码是可运行的。我只是不知道如何优化它。
    • 重点是,这不被支持。您的程序可能会崩溃,您的数据可能会损坏,其他任何事情都可能发生。无论如何,从概念上讲,尝试同时写入同一个数据库中的不同表而不加锁是没有意义的。您的表要么是相关的,因此需要锁定,要么它们不是相关的,它们根本不需要在同一个数据库中。是哪个?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-10
    • 2018-12-18
    • 1970-01-01
    • 2021-06-08
    • 2021-10-13
    • 1970-01-01
    • 2023-01-28
    相关资源
    最近更新 更多