我正在研究用於標記文件的C和SQL終端應用程序。文件/標籤關係保存在SQLite 3數據庫中。直到現在,我一直使用靜態分配的準備語句,這些語句在每次調用封裝函數時都會被重置和綁定。最近我用valgrind
測試了內存泄漏等問題,並且這個問題非常抱怨,因爲在程序終止時,封裝器中的靜態sqlite3_stmt
顯然不會被釋放。這也會導致數據庫不能正確關閉(因爲未完成的語句)。SQLite C中的靜態和非靜態準備狀態API
這樣做的最初原因是性能。我在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
?會是醜陋/奇怪嗎?