mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
213 lines
6.4 KiB
C++
213 lines
6.4 KiB
C++
// Copyright (c) 2013, Cornell University
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of HyperLevelDB nor the names of its contributors may
|
|
// be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
// C
|
|
#include <cstdlib>
|
|
|
|
// STL
|
|
#include <tr1/memory>
|
|
#include <vector>
|
|
|
|
// LevelDB
|
|
#include <hyperleveldb/cache.h>
|
|
#include <hyperleveldb/db.h>
|
|
#include <hyperleveldb/filter_policy.h>
|
|
|
|
// po6
|
|
#include <po6/io/fd.h>
|
|
#include <po6/threads/thread.h>
|
|
|
|
// e
|
|
#include <e/guard.h>
|
|
|
|
// armnod
|
|
#include <armnod.h>
|
|
|
|
// numbers
|
|
#include <numbers.h>
|
|
|
|
static long _done = 0;
|
|
static long _number = 1000000;
|
|
static long _threads = 1;
|
|
|
|
static struct poptOption _popts[] = {
|
|
{"number", 'n', POPT_ARG_LONG, &_number, 0,
|
|
"perform N operations against the database (default: 1000000)", "N"},
|
|
{"threads", 't', POPT_ARG_LONG, &_threads, 0,
|
|
"run the test with T concurrent threads (default: 1)", "T"},
|
|
POPT_TABLEEND
|
|
};
|
|
|
|
static void
|
|
worker_thread(leveldb::DB*,
|
|
numbers::throughput_latency_logger* tll,
|
|
const armnod::argparser& k,
|
|
const armnod::argparser& v);
|
|
|
|
int
|
|
main(int argc, const char* argv[])
|
|
{
|
|
armnod::argparser key_parser("key-");
|
|
armnod::argparser value_parser("value-");
|
|
std::vector<poptOption> popts;
|
|
poptOption s[] = {POPT_AUTOHELP {NULL, 0, POPT_ARG_INCLUDE_TABLE, _popts, 0, "Benchmark:", NULL}, POPT_TABLEEND};
|
|
popts.push_back(s[0]);
|
|
popts.push_back(s[1]);
|
|
popts.push_back(key_parser.options("Key Generation:"));
|
|
popts.push_back(value_parser.options("Value Generation:"));
|
|
popts.push_back(s[2]);
|
|
poptContext poptcon;
|
|
poptcon = poptGetContext(NULL, argc, argv, &popts.front(), POPT_CONTEXT_POSIXMEHARDER);
|
|
e::guard g = e::makeguard(poptFreeContext, poptcon); g.use_variable();
|
|
poptSetOtherOptionHelp(poptcon, "[OPTIONS]");
|
|
|
|
int rc;
|
|
|
|
while ((rc = poptGetNextOpt(poptcon)) != -1)
|
|
{
|
|
switch (rc)
|
|
{
|
|
case POPT_ERROR_NOARG:
|
|
case POPT_ERROR_BADOPT:
|
|
case POPT_ERROR_BADNUMBER:
|
|
case POPT_ERROR_OVERFLOW:
|
|
std::cerr << poptStrerror(rc) << " " << poptBadOption(poptcon, 0) << std::endl;
|
|
return EXIT_FAILURE;
|
|
case POPT_ERROR_OPTSTOODEEP:
|
|
case POPT_ERROR_BADQUOTE:
|
|
case POPT_ERROR_ERRNO:
|
|
default:
|
|
std::cerr << "logic error in argument parsing" << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
leveldb::Options opts;
|
|
opts.create_if_missing = true;
|
|
opts.write_buffer_size = 64ULL * 1024ULL * 1024ULL;
|
|
opts.filter_policy = leveldb::NewBloomFilterPolicy(10);
|
|
leveldb::DB* db;
|
|
leveldb::Status st = leveldb::DB::Open(opts, "tmp", &db);
|
|
|
|
if (!st.ok())
|
|
{
|
|
std::cerr << "could not open LevelDB: " << st.ToString() << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
numbers::throughput_latency_logger tll;
|
|
|
|
if (!tll.open("benchmark.log"))
|
|
{
|
|
std::cerr << "could not open log: " << strerror(errno) << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
typedef std::tr1::shared_ptr<po6::threads::thread> thread_ptr;
|
|
std::vector<thread_ptr> threads;
|
|
|
|
for (size_t i = 0; i < _threads; ++i)
|
|
{
|
|
thread_ptr t(new po6::threads::thread(std::tr1::bind(worker_thread, db, &tll, key_parser, value_parser)));
|
|
threads.push_back(t);
|
|
t->start();
|
|
}
|
|
|
|
for (size_t i = 0; i < threads.size(); ++i)
|
|
{
|
|
threads[i]->join();
|
|
}
|
|
|
|
std::string tmp;
|
|
if (db->GetProperty("leveldb.stats", &tmp)) std::cout << tmp << std::endl;
|
|
delete db;
|
|
|
|
if (!tll.close())
|
|
{
|
|
std::cerr << "could not close log: " << strerror(errno) << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static uint64_t
|
|
get_random()
|
|
{
|
|
po6::io::fd sysrand(open("/dev/urandom", O_RDONLY));
|
|
|
|
if (sysrand.get() < 0)
|
|
{
|
|
return 0xcafebabe;
|
|
}
|
|
|
|
uint64_t ret;
|
|
|
|
if (sysrand.read(&ret, sizeof(ret)) != sizeof(ret))
|
|
{
|
|
return 0xdeadbeef;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
worker_thread(leveldb::DB* db,
|
|
numbers::throughput_latency_logger* tll,
|
|
const armnod::argparser& _k,
|
|
const armnod::argparser& _v)
|
|
{
|
|
armnod::generator key(armnod::argparser(_k).config());
|
|
armnod::generator val(armnod::argparser(_v).config());
|
|
key.seed(get_random());
|
|
val.seed(get_random());
|
|
numbers::throughput_latency_logger::thread_state ts;
|
|
tll->initialize_thread(&ts);
|
|
|
|
while (__sync_fetch_and_add(&_done, 1) < _number)
|
|
{
|
|
std::string k = key();
|
|
std::string v = val();
|
|
|
|
// issue a "get"
|
|
std::string tmp;
|
|
leveldb::ReadOptions ropts;
|
|
leveldb::Status rst = db->Get(ropts, leveldb::Slice(k.data(), k.size()), &tmp);
|
|
assert(rst.ok() || rst.IsNotFound());
|
|
|
|
// issue a "put"
|
|
leveldb::WriteOptions wopts;
|
|
wopts.sync = false;
|
|
tll->start(&ts, 0);
|
|
leveldb::Status wst = db->Put(wopts, leveldb::Slice(k.data(), k.size()), leveldb::Slice(v.data(), v.size()));
|
|
tll->finish(&ts);
|
|
assert(wst.ok());
|
|
}
|
|
|
|
tll->terminate_thread(&ts);
|
|
}
|