From 5fe16cf194d8cffb75bbf18dc4678c87a6c319a0 Mon Sep 17 00:00:00 2001 From: Christoph Rupp Date: Tue, 24 Sep 2013 21:42:37 +0200 Subject: [PATCH] More fixes and improved graphs --- new/Makefile.am | 1 + new/berkeleydb.cc | 2 +- new/berkeleydb.h | 2 +- new/database.h | 2 +- new/generator.h | 68 ++++------------- new/generator_parser.h | 1 + new/generator_runtime.cc | 70 +++++++++++++---- new/generator_runtime.h | 9 ++- new/gnuplot-lat | 5 +- new/gnuplot-ops | 5 +- new/graph.h | 159 +++++++++++++++++++++++++++++++++++++++ new/hamsterdb.h | 4 +- new/main.cc | 6 -- 13 files changed, 241 insertions(+), 93 deletions(-) create mode 100644 new/graph.h diff --git a/new/Makefile.am b/new/Makefile.am index 1ac0833..22afa3f 100644 --- a/new/Makefile.am +++ b/new/Makefile.am @@ -20,6 +20,7 @@ test_SOURCES = berkeleydb.h \ generator_runtime.cc \ getopts.h \ getopts.c \ + graph.h \ hamsterdb.h \ hamsterdb.cc \ main.cc diff --git a/new/berkeleydb.cc b/new/berkeleydb.cc index 7ad54c4..43f4640 100644 --- a/new/berkeleydb.cc +++ b/new/berkeleydb.cc @@ -76,7 +76,7 @@ compare_db64(DB *db, const DBT *dbt1, const DBT *dbt2, size_t *) } void -BerkeleyDatabase::get_metrics(Metrics *metrics) +BerkeleyDatabase::get_metrics(Metrics *metrics, bool live) { } diff --git a/new/berkeleydb.h b/new/berkeleydb.h index 3e42229..a99b4d1 100644 --- a/new/berkeleydb.h +++ b/new/berkeleydb.h @@ -41,7 +41,7 @@ class BerkeleyDatabase : public Database } // Fills |metrics| with additional metrics - virtual void get_metrics(Metrics *metrics); + virtual void get_metrics(Metrics *metrics, bool live = false); protected: // the actual implementation(s) diff --git a/new/database.h b/new/database.h index 49a314b..812dfb0 100644 --- a/new/database.h +++ b/new/database.h @@ -89,7 +89,7 @@ class Database ham_status_t cursor_close(Cursor *cursor); // Fills |metrics| with additional metrics - virtual void get_metrics(Metrics *metrics) = 0; + virtual void get_metrics(Metrics *metrics, bool live = false) = 0; protected: // the actual implementation(s) diff --git a/new/generator.h b/new/generator.h index 55c55ba..f54036d 100644 --- a/new/generator.h +++ b/new/generator.h @@ -12,8 +12,8 @@ #ifndef GENERATOR_H__ #define GENERATOR_H__ -#include "metrics.h" #include "database.h" +#include "graph.h" class Configuration; @@ -25,8 +25,8 @@ class Generator public: enum { kCommandInsert = 0, - kCommandErase = 1, - kCommandFind = 2, + kCommandFind = 1, + kCommandErase = 2, kCommandCommitTransaction = 3, kCommandCreate, kCommandOpen, @@ -40,50 +40,23 @@ class Generator // constructor Generator(int id, Configuration *conf, Database *db) - : m_id(id), m_config(conf), m_db(db), m_last_status(0), - m_opspersec_graph(0) { + : m_id(id), m_config(conf), m_db(db), m_last_status(0), m_graph(0) { memset(&m_record, 0, sizeof(m_record)); - memset(&m_opspersec, 0, sizeof(m_opspersec)); - memset(&m_latency_graphs, 0, sizeof(m_latency_graphs)); // only create graph output for the first hamsterdb thread! if (conf->metrics >= Configuration::kMetricsPng - && !strcmp(db->get_name(), "hamsterdb")) { - if (id == 0) { - for (int i = 0; i < 4; i++) { - char filename[128]; - sprintf(filename, "%s-lat%d.dat", m_db->get_name(), i); - m_latency_graphs[i] = fopen(filename, "w"); - if (!m_latency_graphs[i]) { - printf("error writing to file: %s\n", strerror(errno)); - exit(-1); - } - setvbuf(m_latency_graphs[i], NULL, _IOFBF, 10 * 1024 * 1024); - } - char filename[128]; - sprintf(filename, "%s-ops.dat", m_db->get_name()); - m_opspersec_graph = fopen(filename, "w"); - if (!m_opspersec_graph) { - printf("error writing to file: %s\n", strerror(errno)); - exit(-1); - } - setvbuf(m_opspersec_graph, NULL, _IOFBF, 2 * 1024 * 1024); - } + && !strcmp(db->get_name(), "hamsterdb") + && id == 0) { + m_graph = new Graph("hamsterdb"); } } // destructor virtual ~Generator() { - for (int i = 0; i < 4; i++) { - if (m_latency_graphs[i]) { - fclose(m_latency_graphs[i]); - m_latency_graphs[i] = 0; - } - } - if (m_opspersec_graph) { - fclose(m_opspersec_graph); - m_opspersec_graph = 0; + if (m_graph) { + delete m_graph; + m_graph = 0; } } @@ -119,17 +92,6 @@ class Generator } protected: - // adds information to the latency graph - void add_latency_graph(int command, double time, double latency) { - fprintf(m_latency_graphs[command], "%f %f\n", time, latency); - } - - // add information to the "operations per second" graph - void add_opspersec_graph(uint64_t elapsed_seconds) { - fprintf(m_opspersec_graph, "%lu %lu %lu %lu %lu\n", elapsed_seconds, - m_opspersec[0], m_opspersec[1], m_opspersec[2], m_opspersec[3]); - } - // unique ID - used to create the database int m_id; @@ -147,15 +109,11 @@ class Generator // different databases ham_record_t m_record; - // file handles for the latency output; index is the command id - // (kCommandInsert, -Erase, -Find, -CommitTransaction) - FILE *m_latency_graphs[4]; - - // file handles for the operation-per-second - FILE *m_opspersec_graph; + // the performance graphs + Graph *m_graph; // accumulating operations-per-seconds for the graphs - uint64_t m_opspersec[4]; + uint32_t m_opspersec[4]; }; #endif /* GENERATOR_H__ */ diff --git a/new/generator_parser.h b/new/generator_parser.h index a30061c..848f0ef 100644 --- a/new/generator_parser.h +++ b/new/generator_parser.h @@ -19,6 +19,7 @@ #include #include "timer.h" +#include "metrics.h" #include "generator.h" #include "database.h" diff --git a/new/generator_runtime.cc b/new/generator_runtime.cc index 937951b..d2ca487 100644 --- a/new/generator_runtime.cc +++ b/new/generator_runtime.cc @@ -147,6 +147,11 @@ RuntimeGenerator::execute() if (m_state == kStateStopped) return (false); + double insert_latency = 0.0; + double erase_latency = 0.0; + double find_latency = 0.0; + double commit_latency = 0.0; + int cmd = get_next_command(); switch (cmd) { case Generator::kCommandCreate: @@ -159,13 +164,13 @@ RuntimeGenerator::execute() close(); break; case Generator::kCommandInsert: - insert(); + insert_latency = insert(); break; case Generator::kCommandErase: - erase(); + erase_latency = erase(); break; case Generator::kCommandFind: - find(); + find_latency = find(); break; case Generator::kCommandBeginTransaction: txn_begin(); @@ -174,7 +179,7 @@ RuntimeGenerator::execute() txn_abort(); break; case Generator::kCommandCommitTransaction: - txn_commit(); + commit_latency = txn_commit(); break; default: assert(!"shouldn't be here"); @@ -185,6 +190,35 @@ RuntimeGenerator::execute() if (m_progress && m_config->limit_ops) (*m_progress) += 1; + // write page fetch/flush graphs? + if (m_graph) { + Metrics m; + m_db->get_metrics(&m); + + double elapsed = m_start.seconds(); + + uint32_t flushes = 0; + uint32_t fetches = 0; + if (m.hamster_metrics.page_count_flushed + > m_metrics.hamster_metrics.page_count_flushed) { + flushes = m.hamster_metrics.page_count_flushed + - m_metrics.hamster_metrics.page_count_flushed; + m_metrics.hamster_metrics.page_count_flushed + = m.hamster_metrics.page_count_flushed; + } + if (m.hamster_metrics.page_count_fetched + > m_metrics.hamster_metrics.page_count_fetched) { + fetches = m.hamster_metrics.page_count_fetched + - m_metrics.hamster_metrics.page_count_fetched; + m_metrics.hamster_metrics.page_count_fetched + = m.hamster_metrics.page_count_fetched; + } + + m_graph->add_latency_metrics(elapsed, insert_latency, find_latency, + erase_latency, commit_latency, fetches, flushes); + } + + return (true); } @@ -239,7 +273,7 @@ RuntimeGenerator::close() m_metrics.elapsed_wallclock_seconds = m_start.seconds(); } -void +double RuntimeGenerator::insert() { ham_key_t key = generate_key(); @@ -257,7 +291,6 @@ RuntimeGenerator::insert() double elapsed = t.seconds(); m_opspersec[kCommandInsert]++; - add_latency_graph(kCommandInsert, m_start.seconds(), elapsed); if (m_metrics.insert_latency_min > elapsed) m_metrics.insert_latency_min = elapsed; @@ -275,9 +308,11 @@ RuntimeGenerator::insert() } m_metrics.insert_ops++; + + return (elapsed); } -void +double RuntimeGenerator::erase() { ham_key_t key = generate_key(); @@ -294,7 +329,6 @@ RuntimeGenerator::erase() double elapsed = t.seconds(); m_opspersec[kCommandErase]++; - add_latency_graph(kCommandErase, m_start.seconds(), elapsed); if (m_metrics.erase_latency_min > elapsed) m_metrics.erase_latency_min = elapsed; @@ -306,9 +340,11 @@ RuntimeGenerator::erase() m_success = false; m_metrics.erase_ops++; + + return (elapsed); } -void +double RuntimeGenerator::find() { ham_key_t key = generate_key(); @@ -327,7 +363,6 @@ RuntimeGenerator::find() double elapsed = t.seconds(); m_opspersec[kCommandFind]++; - add_latency_graph(kCommandFind, m_start.seconds(), elapsed); if (m_metrics.find_latency_min > elapsed) m_metrics.find_latency_min = elapsed; @@ -340,6 +375,8 @@ RuntimeGenerator::find() m_metrics.find_bytes += m_record.size; m_metrics.find_ops++; + + return (elapsed); } void @@ -381,7 +418,7 @@ RuntimeGenerator::txn_abort() m_metrics.other_ops++; } -void +double RuntimeGenerator::txn_commit() { tee("TXN_COMMIT"); @@ -400,7 +437,6 @@ RuntimeGenerator::txn_commit() double elapsed = t.seconds(); m_opspersec[kCommandCommitTransaction]++; - add_latency_graph(kCommandCommitTransaction, m_start.seconds(), elapsed); if (m_metrics.txn_commit_latency_min > elapsed) m_metrics.txn_commit_latency_min = elapsed; @@ -412,6 +448,7 @@ RuntimeGenerator::txn_commit() m_success = false; m_metrics.txn_commit_ops++; + return (elapsed); } ham_key_t @@ -496,15 +533,16 @@ RuntimeGenerator::limit_reached() return (true); } - // reached time limit? - if (m_config->limit_seconds - || m_config->metrics >= Configuration::kMetricsPng) { + // reached time limit and/or update latency graphs? + if (m_config->limit_seconds || m_graph) { double new_elapsed = m_start.seconds(); if (new_elapsed - m_elapsed_seconds >= 1.) { if (m_progress) (*m_progress) += (unsigned)(new_elapsed - m_elapsed_seconds); m_elapsed_seconds = new_elapsed; - add_opspersec_graph(m_elapsed_seconds); + if (m_graph) + m_graph->add_opspersec_graph(m_elapsed_seconds, m_opspersec[0], + m_opspersec[1], m_opspersec[2], m_opspersec[3]); memset(&m_opspersec, 0, sizeof(m_opspersec)); } if (m_config->limit_seconds && new_elapsed > m_config->limit_seconds) { diff --git a/new/generator_runtime.h b/new/generator_runtime.h index 31b3ce4..f358e36 100644 --- a/new/generator_runtime.h +++ b/new/generator_runtime.h @@ -18,6 +18,7 @@ #include #include +#include "metrics.h" #include "timer.h" #include "generator.h" #include "datasource.h" @@ -73,19 +74,19 @@ class RuntimeGenerator : public Generator void create(); // inserts a key/value pair - void insert(); + double insert(); // erases a key/value pair - void erase(); + double erase(); // lookup of a key/value pair - void find(); + double find(); // begins a new transaction void txn_begin(); // commits a transaction - void txn_commit(); + double txn_commit(); // aborts a transaction void txn_abort(); diff --git a/new/gnuplot-lat b/new/gnuplot-lat index 483035d..af7de4d 100644 --- a/new/gnuplot-lat +++ b/new/gnuplot-lat @@ -3,7 +3,4 @@ set terminal png set xlabel "time" set ylabel "latency (thread #1)" set style data linespoint -plot "hamsterdb-lat0.dat" using 1:2 title "insert", \ -"hamsterdb-lat1.dat" using 1:2 title "erase", \ -"hamsterdb-lat2.dat" using 1:2 title "find", \ -"hamsterdb-lat3.dat" using 1:2 title "txn-commit" +plot "hamsterdb-lat.dat" using 1:2 title "insert" diff --git a/new/gnuplot-ops b/new/gnuplot-ops index 0846ebb..040725d 100644 --- a/new/gnuplot-ops +++ b/new/gnuplot-ops @@ -3,7 +3,4 @@ set terminal png set xlabel "time" set ylabel "operations (all threads)" set style data linespoint -plot "hamsterdb-ops.dat" using 1:2 title "insert", \ -"" using 1:3 title "erase", \ -"" using 1:4 title "find", \ -"" using 1:5 title "txn-commit" +plot "hamsterdb-ops.dat" using 1:2 title "insert" diff --git a/new/graph.h b/new/graph.h new file mode 100644 index 0000000..5e0625d --- /dev/null +++ b/new/graph.h @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2005-2013 Christoph Rupp (chris@crupp.de). + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See files COPYING.* for License information. + */ + +#ifndef GRAPH_H__ +#define GRAPH_H__ + +#include +#include +#include + +#include + +// +// A class which writes a PNG graph +// +class Graph +{ + public: + // constructor + Graph(const char *name) + : m_name(name), m_latency_file(0), m_opspersec_file(0), + m_has_lat_inserts(false), m_has_lat_finds(false), + m_has_lat_erases(false), m_has_lat_commits(false) { + } + + // destructor - creates the PNG file + virtual ~Graph() { + generate_png(); + if (m_latency_file) { + fclose(m_latency_file); + m_latency_file = 0; + } + if (m_opspersec_file) { + fclose(m_opspersec_file); + m_opspersec_file = 0; + } + } + + // add information to the "operations per second" graph + void add_opspersec_graph(uint64_t time, uint32_t insert, uint32_t find, + uint32_t erase, uint32_t commit) { + if (!m_opspersec_file) { + char filename[128]; + sprintf(filename, "%s-ops.dat", m_name.c_str()); + m_opspersec_file = fopen(filename, "w"); + if (!m_opspersec_file) { + printf("error writing to file: %s\n", strerror(errno)); + exit(-1); + } + setvbuf(m_opspersec_file, NULL, _IOFBF, 2 * 1024 * 1024); + } + fprintf(m_opspersec_file, "%lu %u %u %u %u\n", time, insert, + find, erase, commit); + } + + // add latency metrics to the graphs + void add_latency_metrics(double time, double lat_insert, double lat_find, + double lat_erase, double lat_commit, uint32_t page_fetch, + uint32_t page_flush) { + if (!m_latency_file) { + char filename[128]; + sprintf(filename, "%s-lat.dat", m_name.c_str()); + m_latency_file = fopen(filename, "w"); + if (!m_latency_file) { + printf("error writing to file: %s\n", strerror(errno)); + exit(-1); + } + setvbuf(m_latency_file, NULL, _IOFBF, 10 * 1024 * 1024); + } + if (lat_insert > 0.0) + m_has_lat_inserts = true; + if (lat_erase > 0.0) + m_has_lat_erases = true; + if (lat_find > 0.0) + m_has_lat_finds = true; + if (lat_commit > 0.0) + m_has_lat_commits = true; + fprintf(m_latency_file, "%f %f %f %f %f %u %u\n", time, lat_insert, + lat_find, lat_erase, lat_commit, page_fetch, page_flush); + } + + // generates a PNG from the accumulated data + void generate_png() { + boost::filesystem::remove("graph-lat.png"); + boost::filesystem::remove("graph-ops.png"); + if (m_latency_file) { + fflush(m_latency_file); + + std::ofstream os; + os.open("gnuplot-lat"); + os << "reset" << std::endl + << "set terminal png" << std::endl + << "set xlabel \"time\"" << std::endl + << "set ylabel \"latency (thread #1)\"" << std::endl + << "set style data linespoint" << std::endl + << "plot \"hamsterdb-lat.dat\" using 1:2 title \"insert\""; + if (m_has_lat_finds) + os << ", \"\" using 1:3 title \"find\""; + if (m_has_lat_erases) + os << ", \"\" using 1:4 title \"erase\""; + if (m_has_lat_commits) + os << ", \"\" using 1:5 title \"txn-commit\""; + os << std::endl; + os.close(); + + system("gnuplot gnuplot-lat > graph-lat.png"); + } + + if (m_opspersec_file) { + fflush(m_opspersec_file); + + std::ofstream os; + os.open("gnuplot-ops"); + os << "reset" << std::endl + << "set terminal png" << std::endl + << "set xlabel \"time\"" << std::endl + << "set ylabel \"operations (all threads)\"" << std::endl + << "set style data linespoint" << std::endl + << "plot \"hamsterdb-ops.dat\" using 1:2 title \"insert\""; + if (m_has_lat_finds) + os << ", \"\" using 1:3 title \"find\""; + if (m_has_lat_erases) + os << ", \"\" using 1:4 title \"erase\""; + if (m_has_lat_commits) + os << ", \"\" using 1:5 title \"txn-commit\""; + os << std::endl; + os.close(); + + system("gnuplot gnuplot-ops > graph-ops.png"); + } + } + + private: + // name: required for the filenames and labels + std::string m_name; + + // file handle for the latency output + FILE *m_latency_file; + + // file handle for the operation-per-second + FILE *m_opspersec_file; + + // flags to decide whether certain graphs are printed + bool m_has_lat_inserts; + bool m_has_lat_finds; + bool m_has_lat_erases; + bool m_has_lat_commits; +}; + +#endif /* GRAPH_H__ */ + diff --git a/new/hamsterdb.h b/new/hamsterdb.h index fa60cdc..7ea37fd 100644 --- a/new/hamsterdb.h +++ b/new/hamsterdb.h @@ -35,7 +35,9 @@ class HamsterDatabase : public Database } // Fills |metrics| with additional metrics - virtual void get_metrics(Metrics *metrics) { + virtual void get_metrics(Metrics *metrics, bool live = false) { + if (live) + ham_env_get_metrics(ms_env, &metrics->hamster_metrics); metrics->hamster_metrics = m_hamster_metrics; } diff --git a/new/main.cc b/new/main.cc index c9e2565..43f196e 100644 --- a/new/main.cc +++ b/new/main.cc @@ -1118,11 +1118,5 @@ main(int argc, char **argv) #endif } - // try to generate the graphs - if (ok && c.metrics >= Configuration::kMetricsPng) { - system("gnuplot gnuplot-ops > graph-ops.png"); - system("gnuplot gnuplot-lat > graph-lat.png"); - } - return (ok ? 0 : 1); }