多处理器线程下优化内存数据库QSql

发布于 2024-12-27 08:13:44 字数 3148 浏览 4 评论 0原文

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

SetThreadAffinityMask (hThread, processorMask);

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

更新:最可能的原因是Qt或/和Sqlite 3内部的某种锁定减慢了我的程序,因此是否可以关闭或通过预设绕过这些锁定。

更新2:这是一个示例。 (可能有点长,抱歉)

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();
}

Here's my problem, I have one sqlite memory database by using QSql. I have several threads each handling one different table of this common database. And I use Win API to make sure these threads working on different processor, like this:

SetThreadAffinityMask (hThread, processorMask);

When there's only thread handling one table, it takes 10 seconds and uses 25% of total CPU. But when there're 4 threads handling 4 different tables, it takes nearly 40 seconds and uses only 35% of total CPU. I think the reason is there's some kind of thread-safe sync in one database. But due to different thread reading or writing different table, thread-safe slows my program. How can I optimize it.

Update: The most possible reason is some kinds of lock inside of Qt or/and Sqlite 3 slows my program, so is it possible to shutdown or bypass these locks by pre-setting.

Update2: Here's an example. (Maybe a little long, sorry)

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();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

萌逼全场 2025-01-03 08:13:44

首先,不要显式设置关联掩码,Windows 会自动在最空闲的核心上分配线程。在这种情况下,最好依靠操作系统来进行线程分配,而不是您的代码。

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

First of all, don't set affinity mask explicitly, windows will automatically allocate threads on the most idle cores. It's better to rely on OS to do thread distribution than your code in this case.

As far as I know, sqlite locks whole database while writing, that's why you don't get a performance boost you expected. Take a look at sqlite locking documentation http://www.sqlite.org/lockingv3.html

绮烟 2025-01-03 08:13:44

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

这可能与线程和锁无关。它可能与阿姆达尔定律有关。

Have you measured how much time the threads are spending on the CPU as compared to, say, disk I/O throughput?

This could have nothing to do with threading and locks. It could have everything to do with Amdahl's law.

下壹個目標 2025-01-03 08:13:44

Qt 文档对此很明确。来自 http://doc.qt。 nokia.com/4.7/threads-modules.html#threads-and-the-sql-module

线程和 SQL 模块

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

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

无法通过 Qt API 完成您想要的操作。

The Qt docs are unambiguous about this. From http://doc.qt.nokia.com/4.7/threads-modules.html#threads-and-the-sql-module:

Threads and the SQL Module

A connection can only be used from within the thread that created it.
Moving connections between threads or creating queries from a
different thread is not supported.

In addition, the third party libraries used by the QSqlDrivers can
impose further restrictions on using the SQL Module in a multithreaded
program. Consult the manual of your database client for more
information

There is no way to do what you want through the Qt API.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文