我正在用C和SQL开发一个终端应用程序来标记文件。文件/标记关系保存在SQLite 3数据库中。到目前为止,我一直在使用静态分配的准备语句,每次调用包装函数时都会对这些语句进行重置和绑定。最近,我正在用valgrind测试内存泄漏等问题,它抱怨很多,因为很明显,包装器中的静态sqlite3_stmt在程序终止时没有释放。这还会导致数据库无法正确关闭(因为未完成的语句)。
这样做的最初原因是性能。我在C接口简介中读到,重用准备好的语句对性能非常好。
在现有的准备语句上使用sqlite3_reset()而不是创建新的准备语句,可以避免不必要的对sqlite3_prepare()的调用。在许多SQL语句中,运行sqlite3_prepare()所需的时间等于或超过sqlite3_step()所需的时间。因此,避免对sqlite3_prepare()的调用可以显著提高性能。
下面是一些代码来展示我正在做的事情:
int tag_file(const char *file, const char *tag)
{
static sqlite3_stmt *sql_prep = NULL;
static const char *sql_str = "INSERT OR IGNORE INTO Tag VALUES (?, ?);";
// Prepare if null, else reset
prepare_or_reset(&sql_prep, sql_str);
if (sqlite3_bind_text(sql_prep, 1, file, -1, SQLITE_STATIC) != SQLITE_OK ||
sqlite3_bind_text(sql_prep, 2, tag, -1, SQLITE_STATIC) != SQLITE_OK)
return ERROR;
if (sqlite3_step(sql_prep) != SQLITE_DONE)
return ERROR;
return SUCCESS;
}我尝试过的(它将满足valgrind,但完全忽略了重用准备语句的性能增益)是使sql_prep非静态,并在函数结束时调用sqlite3_finalize(sql_prep),但我认为这在性能上更糟糕。我可以只准备一次语句,而不会以一种优雅的方式泄漏内存?
我开始担心的另一件事是内存消耗。在未来,我计划为这个应用程序创建一个GUI,这将使所有这些在内存中保存得更久。性能的提高可能会更大,但从第一次调用退出开始,该语句就会出现在堆上。这是公平的空时贸易吗?
编辑:将所有“静态”准备好的语句保存在全局数组中(我可以释放atexit)是否很难看/很奇怪?
发布于 2014-03-04 09:24:03
tag_file )之外不知道语句,则这是不可能的。您应该有一个全局语句列表,并让prepare_or_reset函数动态地将语句添加到该列表中。发布于 2014-03-03 23:20:16
你说的是两个问题。
int tag_file(const char *file, const char *tag)函数似乎是合适的。
要么您将stmt保持在更高的级别,然后每次将它传递给函数。然后你就可以在程序的最后清理。
另一种方法是定义函数的语义,为其提供特殊的哨兵函数(可能只是(NULL, NULL)),从而使函数释放语句。https://stackoverflow.com/questions/22157714
复制相似问题