多处理器线程下优化内存数据库QSql
这是我的问题,我使用 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
首先,不要显式设置关联掩码,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
您是否测量过线程在 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.
Qt 文档对此很明确。来自 http://doc.qt。 nokia.com/4.7/threads-modules.html#threads-and-the-sql-module:
无法通过 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:
There is no way to do what you want through the Qt API.