Unverified Commit 33e10c84 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge #2871

Fake work for resolution models testing.
parents 3a382331 e4870b2e
......@@ -1201,6 +1201,7 @@ AC_CONFIG_FILES([Makefile
src/bin/dhcp4/tests/Makefile
src/bin/resolver/Makefile
src/bin/resolver/tests/Makefile
src/bin/resolver/bench/Makefile
src/bin/sysinfo/Makefile
src/bin/sockcreator/Makefile
src/bin/sockcreator/tests/Makefile
......
SUBDIRS = . tests
SUBDIRS = . tests bench
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
......
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += -I$(top_builddir)/src/bin/resolver
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
noinst_PROGRAMS = resolver-bench
resolver_bench_SOURCES = main.cc
resolver_bench_SOURCES += fake_resolution.h fake_resolution.cc
resolver_bench_SOURCES += dummy_work.h dummy_work.cc
resolver_bench_SOURCES += naive_resolver.h naive_resolver.cc
resolver_bench_LDADD = $(GTEST_LDADD)
resolver_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
resolver_bench_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <resolver/bench/dummy_work.h>
namespace isc {
namespace resolver {
namespace bench {
void
dummy_work() {
// Function left intentonally blank.
};
}
}
}
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef DUMMY_WORK_H
#define DUMMY_WORK_H
namespace isc {
namespace resolver {
namespace bench {
/// \brief An empty function.
///
/// An empty function, to fill the CPU with something during the benchmark.
/// It is expected to be called many times by whatever simulates doing some
/// real CPU-bound work.
///
/// It is defined in separate translation unit, so the compiler does not
/// know it is empty and can't optimise the call out.
void dummy_work();
}
}
}
#endif
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <resolver/bench/fake_resolution.h>
#include <resolver/bench/dummy_work.h>
#include <asiolink/interval_timer.h>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <algorithm>
#include <cstdlib>
namespace isc {
namespace resolver {
namespace bench {
// Parameters of the generated queries.
// How much work is each operation?
const size_t parse_size = 100000;
const size_t render_size = 100000;
const size_t send_size = 1000;
const size_t cache_read_size = 10000;
const size_t cache_write_size = 10000;
// How large a change is to terminate in this iteration (either by getting
// the complete answer, or by finding it in the cache). With 0.5, half the
// queries are found in the cache directly. Half of the rest needs just one
// upstream query. Etc.
const float chance_complete = 0.5;
// Number of milliseconds an upstream query can take. It picks a random number
// in between.
const size_t upstream_time_min = 2;
const size_t upstream_time_max = 50;
FakeQuery::FakeQuery(FakeInterface& interface) :
interface_(&interface),
outstanding_(false)
{
// Schedule what tasks are needed.
// First, parse the query
steps_.push_back(Step(Compute, parse_size));
// Look into the cache if it is there
steps_.push_back(Step(CacheRead, cache_read_size));
while ((1.0 * random()) / RAND_MAX > chance_complete) {
// Needs another step of recursion. Render the upstream query.
steps_.push_back(Step(Compute, render_size));
// Send it and wait for the answer.
steps_.push_back(Step(Upstream, upstream_time_min +
(random() *
(upstream_time_max - upstream_time_min) /
RAND_MAX)));
// After it comes, parse the answer and store it in the cache.
steps_.push_back(Step(Compute, parse_size));
steps_.push_back(Step(CacheWrite, cache_write_size));
}
// Last, render the answer and send it.
steps_.push_back(Step(Compute, render_size));
steps_.push_back(Step(Send, send_size));
// Reverse it, so we can pop_back the tasks as we work on them.
std::reverse(steps_.begin(), steps_.end());
}
void
FakeQuery::performTask(const StepCallback& callback) {
// nextTask also does all the sanity checking we need.
if (nextTask() == Upstream) {
outstanding_ = true;
interface_->scheduleUpstreamAnswer(this, callback,
steps_.back().second);
steps_.pop_back();
} else {
for (size_t i = 0; i < steps_.back().second; ++i) {
dummy_work();
}
steps_.pop_back();
callback();
}
}
FakeInterface::FakeInterface(size_t query_count) :
queries_(query_count)
{
BOOST_FOREACH(FakeQueryPtr& query, queries_) {
query = FakeQueryPtr(new FakeQuery(*this));
}
}
void
FakeInterface::processEvents() {
service_.run_one();
}
namespace {
void
processDone(bool* flag) {
*flag = true;
}
}
FakeQueryPtr
FakeInterface::receiveQuery() {
// Handle all the events that are already scheduled.
// As processEvents blocks until an event happens and we want to terminate
// if there are no events, we do a small trick. We post an event to the end
// of the queue and work until it is found. This should process all the
// events that were there already.
bool processed = false;
service_.post(boost::bind(&processDone, &processed));
while (!processed) {
processEvents();
}
// Now, look if there are more queries to return.
if (queries_.empty()) {
return (FakeQueryPtr());
} else {
// Take from the back. The order doesn't matter and it's faster from
// there.
FakeQueryPtr result(queries_.back());
queries_.pop_back();
return (result);
}
}
class FakeInterface::UpstreamQuery {
public:
UpstreamQuery(FakeQuery* query, const FakeQuery::StepCallback& callback,
const boost::shared_ptr<asiolink::IntervalTimer> timer) :
query_(query),
callback_(callback),
timer_(timer)
{}
void trigger() {
query_->outstanding_ = false;
callback_();
// We are not needed any more.
delete this;
}
private:
FakeQuery* const query_;
const FakeQuery::StepCallback callback_;
// Just to hold it alive before the callback is called.
const boost::shared_ptr<asiolink::IntervalTimer> timer_;
};
void
FakeInterface::scheduleUpstreamAnswer(FakeQuery* query,
const FakeQuery::StepCallback& callback,
size_t msec)
{
const boost::shared_ptr<asiolink::IntervalTimer>
timer(new asiolink::IntervalTimer(service_));
UpstreamQuery* q(new UpstreamQuery(query, callback, timer));
timer->setup(boost::bind(&UpstreamQuery::trigger, q), msec);
}
}
}
}
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef FAKE_RESOLUTION_H
#define FAKE_RESOLUTION_H
#include <exceptions/exceptions.h>
#include <asiolink/io_service.h>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <utility>
#include <vector>
namespace isc {
namespace resolver {
namespace bench {
/// \brief The kind of task a FakeQuery might want to perform.
///
/// The benchmark should examine which kind of task the query needs to perform
/// to progress forward. According to the task, some resources might need to be
/// locked, something re-scheduled, or such.
enum Task {
/// \brief Some CPU-bound computation.
///
/// The query needs to do some computation without any shared resources.
/// This might be parsing or rendering of the query, verification of
/// signatures, etc.
Compute,
/// \brief The query needs to read data from cache.
CacheRead,
/// \brief The query needs to modify the cache.
CacheWrite,
/// \brief A response is to be sent.
///
/// This needs to access the interface/socket. If the socket is shared
/// between threads, it might need to lock it.
Send,
/// \brief An answer from upstream server is needed.
///
/// The query needs to send a query to some authoritative server and wait
/// for the answer. Something might need to be locked (or not, depending
/// on the architecture of the thing that sends and receives). Also, the
/// task will not complete immediately, the callback of performTask
/// will be called at later time.
Upstream
};
class FakeInterface;
/// \brief Imitation of the work done to resolve a query.
///
/// An object of this class represents some fake work that should look like
/// the work needed to perform resolution of one query. No real work is done,
/// but several steps are scheduled, with characteristics hopefully
/// corresponding to steps of the real query.
///
/// The idea is that benchmark will repeatedly check if the query is done.
/// If not, it examines the next task by calling nextTask(). Depending on
/// the result, it'd lock or prepare any shared resources. After that, it'd
/// call performTask() to do the task. Once the query calls the callback
/// passed, it can proceed to the next step.
///
/// See naive_resolver.cc for example code how this could be done.
class FakeQuery {
private:
// The queries come only through an interface. Don't let others create.
friend class FakeInterface;
/// \brief Constructor
FakeQuery(FakeInterface& interface);
public:
/// \brief Is work on the query completely done?
///
/// If this returns true, do not call performTask or nextTask any more.
/// The resolution is done.
///
/// \throw isc::InvalidOperation if upstream query is still in progress.
bool done() const {
if (outstanding_) {
isc_throw(isc::InvalidOperation, "Upstream query outstanding");
}
return (steps_.empty());
}
/// \brief Callback to signify a task has been performed.
typedef boost::function<void()> StepCallback;
/// \brief Perform next step in the resolution.
///
/// Do whatever is needed to be done for the next step of resolution.
/// Once the step is done, the callback is called.
///
/// The callback is usually called from within this call. However, in
/// the case when the nextTask() returned `Upstream`, the call to the
/// callback is delayed for some period of time after the method
/// returns.
///
/// \throw isc::InvalidOperation if it is called when done() is true, or
/// if an upstream query is still in progress (performTask was called
/// before and the callback was not called by the query yet).
void performTask(const StepCallback& callback);
/// \brief Examine the kind of the next resolution process.
///
/// Call this to know what kind of task will performTask do next.
///
/// \throw isc::InvalidOperation if it is called when done() is true, or
/// if an upstream query is still in progress (performTask was called
/// before and the callback was not called by the query yet).
Task nextTask() const {
// Will check for outstanding_ internally too
if (done()) {
isc_throw(isc::InvalidOperation, "We are done, no more tasks");
}
return (steps_.back().first);
}
/// \brief Move network communication to different interface.
///
/// By default, a query does all the "communication" on the interface
/// it was born on. This may be used to move a query from one interface
/// to another.
///
/// You don't have to lock either of the interfaces to do so, this
/// only switches the data in the query.
///
/// \throw isc::InvalidOperation if it is called while an upstream query
/// is in progress.
void migrateTo(FakeInterface& dst_interface) {
if (outstanding_) {
isc_throw(isc::InvalidOperation,
"Can't migrate in the middle of query");
}
interface_ = &dst_interface;
}
private:
// The scheduled steps for this task.
typedef std::pair<Task, size_t> Step;
// The scheduled steps. Reversed (first to be done at the end), so we can
// pop_back() the completed steps.
std::vector<Step> steps_;
// The interface to schedule timeouts on.
FakeInterface* interface_;
// Is an upstream query outstanding?
bool outstanding_;
};
typedef boost::shared_ptr<FakeQuery> FakeQueryPtr;
/// \brief An imitation of interface for receiving queries.
///
/// This is effectively a little bit smarter factory for queries. You can
/// request a new query from it, or let process events (incoming answers).
///
/// It contains its own event loop. If the benchmark has more threads, have
/// one in each of the threads (if the threads ever handles network
/// communication -- if it accepts queries, sends answers or does upstream
/// queries).
///
/// If the model simulated would share the same interface between multiple
/// threads, it is better to have one in each thread as well, but lock
/// access to receiveQuery() so only one is used at once (no idea what happens
/// if ASIO loop is accessed from multiple threads).
///
/// Note that the creation of the queries is not thread safe (due to
/// the random() function inside). The interface generates all its queries
/// in advance, on creation time. But you need to create all the needed
/// interfaces from single thread and then distribute them to your threads.
class FakeInterface {
public:
/// \brief Constructor
///
/// Initiarile the interface and create query_count queries for the
/// benchmark. They will be handed out one by one with receiveQuery().
FakeInterface(size_t query_count);
/// \brief Wait for answers from upstream servers.
///
/// Wait until at least one "answer" comes from the remote server. This
/// will effectively block the calling thread until it is time to call
/// a callback of performTask.
///
/// It is not legal to call it without any outstanding upstream queries
/// on this interface. However, the situation is not explicitly checked.
///
/// \note Due to internal implementation, it is not impossible no or more
/// than one callbacks to be called from within this method.
void processEvents();
/// \brief Accept another query.
///
/// Generate a new fake query to resolve.
///
/// This method might call callbacks of other queries waiting for upstream
/// answer.
///
/// This returns a NULL pointer when there are no more queries to answer
/// (the number designated for the benchmark was reached).
FakeQueryPtr receiveQuery();
private:
class UpstreamQuery;
friend class FakeQuery;
void scheduleUpstreamAnswer(FakeQuery* query,
const FakeQuery::StepCallback& callback,
size_t msec);
asiolink::IOService service_;
std::vector<FakeQueryPtr> queries_;
};
}
}
}
#endif
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <resolver/bench/naive_resolver.h>
#include <bench/benchmark.h>
const size_t count = 1000; // TODO: We may want to read this from argv.
int main(int, const char**) {
// Run the naive implementation
isc::resolver::bench::NaiveResolver naive_resolver(count);
isc::bench::BenchMark<isc::resolver::bench::NaiveResolver>
(1, naive_resolver, true);
return 0;
}
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <resolver/bench/naive_resolver.h>
#include <cassert>
#include <boost/bind.hpp>
namespace isc {
namespace resolver {
namespace bench {
NaiveResolver::NaiveResolver(size_t query_count) :
interface_(query_count),
processed_(false)
{}
namespace {
void
stepDone(bool* flag) {
*flag = true;
}
}
size_t
NaiveResolver::run() {
assert(!processed_);
size_t count = 0;
FakeQueryPtr query;
// Process a query at a time. As the previous is already handled, the
// receiveQuery may never trigger other events.
while ((query = interface_.receiveQuery())) {
// Handle each step
while (!query->done()) {
bool done = false; // This step is not yet done.
// If there were more queries/threads/whatever, we would examine
// the query->nextTask() and lock or prepare resources accordingly.
// But as there's just one, we simply do the task, without caring.
query->performTask(boost::bind(&stepDone, &done));
// We may need to wait for the upstream query.
while (!done) {
interface_.processEvents();
}
}
count ++;
}
processed_ = true;
return (count);
}
}
}
}
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef RESOLVER_BENCH_NAIVE_H
#define RESOLVER_BENCH_NAIVE_H