Google LevelDBは超進化している

実は今はメインはKyoto Cabinetを使っていて,時間性能や空間性能にとても満足しているので紹介したいのですが,GoogleのKVSも進化しているので評価が大変です.

前回,Google LevelDBとRedisを比較した時は,同時アクセス数が多くなるとRedisの方が速かったです.
Google LevelDBのシングルスレッドライト - なぜか数学者にはワイン好きが多い

さっき,Google LevelDBのソースツリーをアップデートしてみると,全く結果が異なりました.

太い黄色と水色の線はRedisと5月末時のLevelDBの結果で,読み出しコア数が8になるとLevelDBの方が遅くなっています.
しかし,茶色のLevelDBリビジョン33では,圧倒的に速くなっています.

リビジョン29の水色からリビジョン33の茶色まで,わずか一ヶ月で超高速化...

Redisは,読み込みも書き込みもスレッド数だけ並列処理しています.

#include "basic_header.h"

int main(int argc, char *argv[])
{
  int suc = 0, i;
  time_t start, set_end, get_end;
  int n = omp_get_max_threads();
  vector<Poco::Net::StreamSocket *> pool(n);
  Poco::Net::SocketAddress server("192.168.1.27", 6379);
  for(vector<Poco::Net::StreamSocket *>::iterator i=pool.begin();i!=pool.end();++i)
  {
    *i = new Poco::Net::StreamSocket(server);
  }
  start = time(NULL);
#pragma omp parallel for shared(suc,i)
  for(i=0;i<MAX_LOOP;++i) {
    Poco::Net::StreamSocket *client=pool.at((int)(i/(MAX_LOOP/n)));
    Poco::MD5Engine con;  string key, val; size_t sent_len, recv_len;
    set_key_val(con, (int)(i/REP), key, val);
    char write_buffer[BUFFER_SIZE], read_buffer[BUFFER_SIZE];
    snprintf(write_buffer, BUFFER_SIZE,
      "*3\r\n$3\r\nSET\r\n$%d\r\n%s\r\n$%d\r\n%s\r\n",key.size(), key.c_str(), val.size(), val.c_str());
    sent_len = client->sendBytes((void *)write_buffer, strlen(write_buffer));
    recv_len = client->receiveBytes(read_buffer, BUFFER_SIZE);
    if (sent_len != strlen(write_buffer) ||strncmp(read_buffer,"+OK\r\n", 5)!=0) {
      fprintf(stderr, "set: %s: redis error %s\n", key.c_str(), read_buffer);
    }else {
      ++suc;
    }
    client = NULL;
  }
  set_end = time(NULL);
#pragma omp parallel for
  for(i=0;i<MAX_LOOP;++i)
  {
    Poco::Net::StreamSocket *client=pool.at((int)(i/(MAX_LOOP/n)));
    Poco::MD5Engine con;  string key, val; size_t sent_len, recv_len;
    set_key_val(con, (int)(i/REP), key, val);
    char write_buffer[BUFFER_SIZE], read_buffer[BUFFER_SIZE];
    char confirm_buffer[BUFFER_SIZE];
    snprintf(write_buffer, BUFFER_SIZE,"*2\r\n$3\r\nGET\r\n$%d\r\n%s\r\n", key.size(), key.c_str());
    snprintf(confirm_buffer, BUFFER_SIZE, "$%d\r\n%s\r\n", val.size(), val.c_str());
    size_t total_len = strlen(confirm_buffer);
    recv_len = 0;
    do  {
      recv_len += client->receiveBytes(read_buffer+recv_len, BUFFER_SIZE);
      read_buffer[recv_len] = '\0';
    }while(recv_len!=0 && recv_len < total_len);
    snprintf(confirm_buffer, BUFFER_SIZE, "$%d\r\n%s\r\n", val.size(), val.c_str());
    if (sent_len != strlen(write_buffer) ||strncmp(read_buffer, confirm_buffer, strlen(confirm_buffer))!=0) {
      printf("get: %s(%d bytes): redis error: %s(%d bytes)\n", val.c_str(),val.size(), read_buffer,strlen(read_buffer));
    }else {
      ++suc;
    }
  }
#include "basic_footer.h"
  return(0);
}

LevelDBの方は,書き込みはシングルスレッドからにしています.

#include "basic_header.h"
#include "leveldb/write_batch.h"
#include "leveldb/db.h"
#include "leveldb/cache.h"

int main()
{

  int suc = 0, i;
  time_t start, set_end, get_end;
  int n = omp_get_max_threads();
  leveldb::DB* db;
  leveldb::Options options;
  options.create_if_missing = true;
  leveldb::Status status = leveldb::DB::Open(options, "/var/tmp/testdb", &db);
  if(!status.ok()) std::cout<<status.ToString()<<std::endl;
  assert(status.ok());

  start = time(NULL);
//#pragma omp parallel for shared(suc,i, db)
  for(i=0;i<MAX_LOOP;++i) {
    Poco::MD5Engine con;    string key, val;
    set_key_val(con, (int)(i/REP), key, val);
   leveldb::WriteOptions write_options;
    status = db->Put(write_options, key, val);
    if(!status.ok()) {
      std::cout<<status.ToString()<<std::endl;
    }else {
      ++suc;
    }
  }
  set_end = time(NULL);

#pragma omp parallel for shared(suc,i, db)
  for(i=0;i<MAX_LOOP;++i) {
    Poco::MD5Engine con;    string key, val, expected_val;
    set_key_val(con, (int)(i/REP), key, expected_val);
    status = db->Get(leveldb::ReadOptions(), key, &val);
    if(!status.ok() || val != expected_val) {
      cout<<"get: "<<val<<" LevelDB error: "<<status.ToString()<<endl;
    }else {
      ++suc;
    }
  }
  //  std::cout<<key<<val<<std::endl;
#include "basic_footer.h"
  delete db;
  return(0);
}