内存中的sqlite3数据库能否比C/C++数据结构更快

问题描述 投票:0回答:2

我有一个管理结构数组的用例。这看起来像一个 SQL 表。它还与基于数组中结构的各种属性的查询相关联。类似于SQL查询。

我想知道 SQLITE 内存数据库是否可以取代我的结构数组,从而为我提供更好的速度和更少的代码,因为我可以重用 SQLITE 功能(例如查询),而不是自己编写函数来遍历数据结构。我希望 SQLITE 的索引能够为我带来额外的速度,而无需重新发明轮子。

但是,在我的初始测试中,SQLITE 内存数据库几乎比 C++ 数据结构慢 12-30 倍,仅用于插入。

我从这个问题中尝试了一些优化,但似乎没有多大帮助。 提高 SQLite 的每秒插入性能

我的问题是 SQLITE 能否击败 C++ 数据结构?

这是两种情况下的示例代码。

使用 C++ 数据结构。

#include <iostream>
#include <map>
#include <cstdlib>
#include <ctime>

struct item_t {
    unsigned int a;
    unsigned int b;
    bool prop1;
    bool prop2;
    bool prop3;
    bool prop4;
    bool prop5;
};

typedef std::map<unsigned int, item_t> items_t;

int main() {
    srand(RAND_SEED);
    items_t data;
    unsigned int delta = 0x100000000 / ITERATIONS, randInt;
    unsigned int count;

    // Create `ITERATIONS segments with random properties
    for (unsigned int i = 0, a = 0; i < ITERATIONS; ++i, a += delta) {
        randInt = rand();
        data[a] = {
            a, a + delta - 1,
            bool(randInt & 16),
            bool(randInt &  8),
            bool(randInt &  4),
            bool(randInt &  2),
            bool(randInt &  1)
        };
    }

    printf("Inserted %d entries\n", data.size());
    return 0;
}

使用 SQLITE 内存数据库

#include <stdio.h>
#include <ctime>
#include <cstdlib>
#include <sqlite3.h>
#include <cassert>
#include <string.h>


static int sqliteExecCallback(void *unused, int count, char **data, char **columns)
{
    for (int idx = 0; idx < count; idx++) {
        printf("%s\n", data[idx]);
    }

    return 0;
}

int main() {
    srand(RAND_SEED);
    unsigned int delta = 0x100000000 / ITERATIONS, randInt;

    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;

    rc = sqlite3_open(":memory:", &db);

    if (rc) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        return(0);
    }
    rc = sqlite3_exec(
        db,
        "PRAGMA synchronous = OFF;"
        "PRAGMA journal_mode = MEMORY",
        NULL, NULL, &zErrMsg
    );
    if (rc) {
        fprintf(stderr, "Can't set PRAGMAs: %s\n", sqlite3_errmsg(db));
        return(0);
    }

    rc = sqlite3_exec(
        db,
        "CREATE TABLE IF NOT EXISTS DATA("
        "a INTEGER PRIMARY KEY,"
        "b INTEGER,"
        "prop1 INTEGER,"
        "prop2 INTEGER,"
        "prop3 INTEGER,"
        "prop4 INTEGER,"
        "prop5 INTEGER"
        ");"
        "CREATE INDEX DATA_prop1_idx on DATA(prop1);"
        "CREATE INDEX DATA_prop2_idx on DATA(prop2);"
        "CREATE INDEX DATA_prop3_idx on DATA(prop3);"
        "CREATE INDEX DATA_prop4_idx on DATA(prop4);"
        "CREATE INDEX DATA_prop5_idx on DATA(prop5);",
        0, 0, &zErrMsg
    );

    if (rc) {
        fprintf(stderr, "Can't execute create table: %s\n", sqlite3_errmsg(db));
        return(0);
    }

    char sql[512] = "INSERT INTO DATA (a, b, prop1, prop2, prop3, prop4, prop5)";
    int len = strlen(sql);
    for (unsigned int i = 0, a = 0; i < ITERATIONS; ++i, a += delta) {
        randInt = rand();
        sprintf(
            sql + len,
            " VALUES (%u, %u, %d, %d, %d, %d, %d);",
            a, a + delta - 1,
            bool(randInt & 16),
            bool(randInt &  8),
            bool(randInt &  4),
            bool(randInt &  2),
            bool(randInt &  1)
        );
        rc = sqlite3_exec(db, sql, 0, 0, &zErrMsg);
    }

    rc = sqlite3_exec(db, "SELECT COUNT(*) FROM DATA", sqliteExecCallback, 0, &zErrMsg);

    if (false && rc) {
        fprintf(stderr, "Can't execute SELECT COUNT(*): %s\n", sqlite3_errmsg(db));
    }
    return 0;
}

运行

g++ -std=c++20 -O3 -march=native -DITERATIONS=2000000 -DRAND_SEED=1 sqlite.cpp -l sqlite3 > comp.log 2>&1
/bin/time -f "sqlite,%S,%U,%e,%M,%X" --append -o metrics.csv ./a.out > run.log 2>&1

g++ -std=c++20 -O3 -march=native -DITERATIONS=2000000 -DRAND_SEED=1 cpp.cpp -l sqlite3 > comp.log 2>&1
/bin/time -f "cpp,%S,%U,%e,%M,%X" --append -o metrics.csv ./a.out > run.log 2>&1

示例运行时间

     -  SysTime  UserTime  RealTime    MaxMem  ExitStatus
   cpp     0.09      0.62      0.71  128928.0         0.0
sqlite     0.06     25.01     25.07  196864.0         0.0
c++ c sqlite c++20
2个回答
0
投票

它总是归结为正在执行的内容。您的代码不是最佳的,因为您的

" VALUES (%u, %u, %d, %d, %d, %d, %d);"

line 确保每个插入都会单独发生。而不是像

这样的命令
insert into sometable(...)
values(...);

你会需要

insert into sometable(...)
values
(...),
(...),
(...),
...
(...);

原因很简单。您运行的每个插入都会转换为一个事务,然后该事务将作为一个事务处理,因此,如果您插入 1000 行,您将有 1000 个单独的事务需要处理,加起来,请参阅 https://sqlite .org/forum/info/f832398c19d30a4a

相反,您应该构建一个要插入的项目队列,并定期构建一批要插入的记录。您可以决定是否希望周期与时间相关(例如每分钟)和/或与大小相关(例如 100 条记录),但由于插入是此处的出血点,因此首先转换您的代码运行批量插入。

然后与C++进行比较。我对内存中 SQLite 是否会比任何其他类型的数据结构更快持怀疑态度,但你绝对可以让它比目前更快。


0
投票

“内存数据库”可能有更快的原因,但这种情况很少见。

首先,最重要的是,你增加了复杂性。也许不是为了你自己,而是为了代码的执行。

现在,当您在 C++ 程序中存储数据时,您通常会查看数据以及需要执行的操作,并选择合适的数据结构来保存它。这通常会给你带来高效的处理——甚至可能非常高效,但通常不值得付出努力来榨取最后一点性能。

数据库并不神奇;如果您的 C++ 代码已经很高效,那么增加大量复杂性并不会提高它。数据库非常灵活,但这是有代价的。您的 C++ 代码不会那么灵活——您必须对每个查询进行硬编码——但它会变得简单明了。有可能,当您的 C++ 代码返回结果时,数据库仍在分析您的查询...

我不知道您的数据库引擎是否可以执行准备好的语句 - 但您是否注意到您正在为每个插入创建一个数据库命令,数据库必须首先解析,然后才能考虑如何执行实际插入?因此,您正在打印一个字符串,数据库会分析该字符串,而在此过程中,C++ 代码已经插入了数据。我们甚至还没有了解数据库表的复杂性。

如果您非常非常懒,将数据库引擎放入您的程序中可以帮助您。就像你只想将你的东西放入一个巨大的 std::vector 并循环它来查找东西;类似的东西可能会帮助您使数据库引擎看起来更好。

© www.soinside.com 2019 - 2024. All rights reserved.