測試環境簡單說明
Windows下測試
硬件環境如下:
處理器:Intel(R) Core(TM) i5-4460 CPU @ 3.20GHz
內 存:8GB
硬 盤:希捷 ST1000DM003
操作系統:Windows 10 企業版
編譯說明:
兩個都是使用VS2015編譯的64位Release版本。運行時庫采用動態多線程版本(MD)
Linux下測試
硬件環境如下:
處理器:Intel(R) Core(TM) i7-4500U CPU @ 1.80GHz
內 存:8GB
硬 盤:金士頓64G SSD
操作系統:ArchLinux (Linux version 4.8.13-1-ARCH)
編譯說明:
兩個都是使用Gcc 6.2.1編譯的x64版本,使用-O2
參數優化。
測試結果
對LevelDB
和ForestDB
進行簡單的性能測試。
兩個都在單線程
下進行10000
次的增刪查改
測試,共測試5次。(這里測試的次數有點少,應該測試十萬次以上的)
測試的時候可以發現(設置斷點),Forest每次操作都將數據緩存在內存了,內存占用比較大。而LevelDB在添加的時候並沒有緩存,但是在數據獲取和修改的時候內存會變大。
總體上LevelDB占用內存小一點,但是linux下速度不及ForestDB(非常接近)。易用程度上,LevelB簡單得多。磁盤占用的情況的話,Forest對磁盤使用比較少,這10000條數據占了13MB左右,而LevelDB則占了120MB左右。
Windows下測試結果
測試結果平均值對比直方圖:
LevelDB 測試結果截圖
ForestDB 測試結果截圖
Linux下測試結果
測試結果平均值對比直方圖:
LevelDB 測試結果截圖
ForestDB 測試結果截圖
測試代碼
LevelDB測試代碼
#include <cassert>
#include <string>
#include <iostream>
#include <chrono>
#include "leveldb/db.h"
#define TEST_FREQUENCY (10000)
char* randomstr()
{
static char buf[1024];
int len = rand() % 768 + 255;
for (int i = 0; i < len; ++i) {
buf[i] = 'A' + rand() % 26;
}
buf[len] = '\0';
return buf;
}
int main()
{
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
// 打開數據庫
leveldb::Status status = leveldb::DB::Open(options, "./testdb", &db);
assert(status.ok());
srand(2017);
std::string k[TEST_FREQUENCY];
for (int i = 0; i < TEST_FREQUENCY; ++i) {
k[i] = (randomstr());
}
std::string v("壹貳叄肆伍陸柒捌玖拾");
v.append(v).append(v).append(v).append(v).append(v);
// 測試添加
{
auto start = std::chrono::system_clock::now();
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = db->Put(leveldb::WriteOptions(), k[i], v);
assert(status.ok());
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次添加耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
// 測試獲取
{
auto start = std::chrono::system_clock::now();
std::string v2[TEST_FREQUENCY];
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = db->Get(leveldb::ReadOptions(), k[i], &v2[i]);
assert(status.ok());
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次獲取耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
// 驗證獲取結果是否正確
std::string ss;
for (int i = 0; i < TEST_FREQUENCY; ++i) {
if (v2[i] != v) {
std::cout << "第 " << i << " 個結果不正確" << std::endl;
std::cout << v2[i] << std::endl;
}
}
}
// 測試修改
{
auto start = std::chrono::system_clock::now();
v.append(v);
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = db->Put(leveldb::WriteOptions(), k[i], v);
assert(status.ok());
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次修改耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
// 測試刪除
{
auto start = std::chrono::system_clock::now();
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = db->Delete(leveldb::WriteOptions(), k[i]);
assert(status.ok());
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次刪除耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
delete db;
return 0;
}
Forest 測試代碼
#include <cassert>
#include <string>
#include <iostream>
#include <chrono>
#include "libforestdb/forestdb.h"
#define TEST_FREQUENCY (10000)
char* randomstr()
{
static char buf[1024];
int len = rand() % 768 + 255;
for (int i = 0; i < len; ++i) {
buf[i] = 'A' + rand() % 26;
}
buf[len] = '\0';
return buf;
}
int main()
{
fdb_file_handle* fdbFileHandle = nullptr;
fdb_kvs_handle* fdbKvsHandle = nullptr;
fdb_status status;
// 初始化ForestDB
// 1、文件配置設置配置
fdb_config fileConfig = fdb_get_default_config();
{// WAL閾值4K
fileConfig.wal_threshold = 4096;
// 緩存大小64MB
fileConfig.buffercache_size = 64 * 1024 * 1024;
// 設置使用默認的kvs
fileConfig.multi_kv_instances = false;
// 關閉循環塊復用
fileConfig.block_reusing_threshold = 100;
// 使用序列樹
fileConfig.seqtree_opt = FDB_SEQTREE_USE;
}
// 2、使用設置的配置進行初始化
status = fdb_init(&fileConfig);
assert(status == FDB_RESULT_SUCCESS);
// 打開數據庫
status = fdb_open(&fdbFileHandle, "./testdb", &fileConfig);
assert(status == FDB_RESULT_SUCCESS);
// 打開kvs
fdb_kvs_config kvsConfig = fdb_get_default_kvs_config();
status = fdb_kvs_open_default(fdbFileHandle, &fdbKvsHandle, &kvsConfig);
assert(status == FDB_RESULT_SUCCESS);
srand(2017);
std::string k[TEST_FREQUENCY];
for (int i = 0; i < TEST_FREQUENCY; ++i) {
k[i] = (randomstr());
}
std::string v("壹貳叄肆伍陸柒捌玖拾");
v.append(v).append(v).append(v).append(v).append(v);
// 測試添加
{
auto start = std::chrono::system_clock::now();
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = fdb_set_kv(fdbKvsHandle, k[i].data(), k[i].size(), v.data(), v.size());
assert(status == FDB_RESULT_SUCCESS);
}
// 提交操作到磁盤(這里必須commit才能實際寫入到磁盤)
fdb_commit(fdbFileHandle, FDB_COMMIT_NORMAL);
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次添加耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
// 測試獲取
{
auto start = std::chrono::system_clock::now();
void* v2[TEST_FREQUENCY]; size_t v2len[TEST_FREQUENCY];
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = fdb_get_kv(fdbKvsHandle, k[i].data(), k[i].size(), &v2[i], &v2len[i]);
assert(status == FDB_RESULT_SUCCESS);
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次獲取耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
// 驗證獲取結果是否正確
std::string ss;
for (int i = 0; i < TEST_FREQUENCY; ++i) {
ss.assign((const char*)v2[i], v2len[i]);
if (ss != v) {
std::cout << "第 " << i << " 個結果不正確" << std::endl;
std::cout << ss << std::endl;
}
free(v2[i]);
}
}
// 測試修改
{
auto start = std::chrono::system_clock::now();
v.append(v);
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = fdb_set_kv(fdbKvsHandle, k[i].data(), k[i].size(), v.data(), v.size());
assert(status == FDB_RESULT_SUCCESS);
}
// 提交操作到磁盤(這里必須commit才能實際寫入到磁盤)
fdb_commit(fdbFileHandle, FDB_COMMIT_NORMAL);
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次修改耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
// 測試刪除
{
auto start = std::chrono::system_clock::now();
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = fdb_del_kv(fdbKvsHandle, k[i].data(), k[i].size());
assert(status == FDB_RESULT_SUCCESS);
}
// 提交操作到磁盤(這里必須commit才能實際寫入到磁盤)
fdb_commit(fdbFileHandle, FDB_COMMIT_NORMAL);
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次刪除耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
// 關閉數據庫
status = fdb_kvs_close(fdbKvsHandle);
assert(status == FDB_RESULT_SUCCESS);
status = fdb_close(fdbFileHandle);
assert(status == FDB_RESULT_SUCCESS);
status = fdb_shutdown();
assert(status == FDB_RESULT_SUCCESS);
return 0;
}