【发布时间】:2014-03-03 21:21:24
【问题描述】:
我正在使用 C 和 SQL 开发一个终端应用程序,用于标记文件。文件/标签关系保存在 SQLite 3 数据库中。到目前为止,我一直在使用静态分配的准备好的语句,这些语句在每次调用包装函数时都会被重置和绑定。最近我正在用valgrind 测试内存泄漏等,它抱怨很多,因为显然包装器中的静态sqlite3_stmt 在程序终止时没有被释放。这也会导致数据库无法正确关闭(因为未完成的语句)。
这样做的最初原因是性能。我在the C interface intro 中读到,重用准备好的语句对性能非常有好处。
在现有的预处理语句上使用 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,这将使所有这些在内存中保存更长时间。性能提升可能会更大,但从第一次调用退出时,该语句将只是坐在堆上。这是公平的space-time tradoff吗?
编辑:将所有“静态”准备好的语句保存在一个全局数组中,我可以释放 atexit,这会是丑陋/奇怪吗?
【问题讨论】: