From: JINMEI Tatuya Date: Thu, 16 Aug 2012 04:29:48 +0000 (-0700) Subject: [2096] added a simple benchmark test for RdataReader. X-Git-Tag: trac2351_base~114^2~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bddce14fdd3f92879bad0fa6f88772e5575b7c02;p=thirdparty%2Fkea.git [2096] added a simple benchmark test for RdataReader. for fair comparison, slightly different implementations are used for now, so the reader.h/cc were copied from the parent directory and modified. --- diff --git a/configure.ac b/configure.ac index e75cbd8d3b..e7b17edc66 100644 --- a/configure.ac +++ b/configure.ac @@ -1165,6 +1165,7 @@ AC_CONFIG_FILES([Makefile src/lib/datasrc/Makefile src/lib/datasrc/memory/Makefile src/lib/datasrc/memory/tests/Makefile + src/lib/datasrc/memory/benchmarks/Makefile src/lib/datasrc/tests/Makefile src/lib/datasrc/tests/testdata/Makefile src/lib/xfr/Makefile diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am index e8a78ea0ca..0ec2add0bb 100644 --- a/src/lib/datasrc/memory/Makefile.am +++ b/src/lib/datasrc/memory/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . tests +SUBDIRS = . tests benchmarks AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns diff --git a/src/lib/datasrc/memory/benchmarks/Makefile.am b/src/lib/datasrc/memory/benchmarks/Makefile.am new file mode 100644 index 0000000000..6741d7faf2 --- /dev/null +++ b/src/lib/datasrc/memory/benchmarks/Makefile.am @@ -0,0 +1,18 @@ +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(B10_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +noinst_PROGRAMS = rdata_reader_bench + +rdata_reader_bench_SOURCES = rdata_reader_bench.cc +rdata_reader_bench_SOURCES += rdata_reader.cc rdata_reader.h + +rdata_reader_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la +rdata_reader_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la diff --git a/src/lib/datasrc/memory/benchmarks/rdata_reader.cc b/src/lib/datasrc/memory/benchmarks/rdata_reader.cc new file mode 100644 index 0000000000..c13a80af6e --- /dev/null +++ b/src/lib/datasrc/memory/benchmarks/rdata_reader.cc @@ -0,0 +1,225 @@ +// Copyright (C) 2012 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 "rdata_reader.h" + +using namespace isc::dns; + +namespace isc { +namespace datasrc { +namespace memory { + +void +RdataReader2::emptyNameAction(const LabelSequence&, unsigned) { + // Do nothing here. On purpose, it's not unfinished. +} + +void +RdataReader2::emptyDataAction(const uint8_t*, size_t) { + // Do nothing here. On purpose, it's not unfinished. +} + +RdataReader2::Result::Result(const LabelSequence& label, + unsigned attributes) : + label_(label), + data_(NULL), + size_(0), + type_(NAME), + compressible_((attributes & NAMEATTR_COMPRESSIBLE) != 0), + additional_((attributes & NAMEATTR_ADDITIONAL) != 0) +{} + +RdataReader2::Result::Result(const uint8_t* data, size_t size) : + label_(Name::ROOT_NAME()), + data_(data), + size_(size), + type_(DATA), + compressible_(false), + additional_(false) +{} + +RdataReader2::RdataReader2(const RRClass& rrclass, const RRType& rrtype, + const uint8_t* data, + size_t rdata_count, size_t sig_count, + const NameAction& name_action, + const DataAction& data_action) : + name_action_(name_action), + data_action_(data_action), + spec_(getRdataEncodeSpec(rrclass, rrtype)), + var_count_total_(spec_.varlen_count * rdata_count), + sig_count_(sig_count), + spec_count_(spec_.field_count * rdata_count), + // The casts, well, C++ decided it doesn't like completely valid + // and explicitly allowed cast in C, so we need to fool it through + // void. + lengths_(static_cast( + static_cast(data))), // The lenghts are stored first + // And the data just after all the lengths + data_(data + (var_count_total_ + sig_count_) * sizeof(uint16_t)), + sigs_(NULL) +{ + rewind(); +} + +void +RdataReader2::rewind() { + data_pos_ = 0; + spec_pos_ = 0; + length_pos_ = 0; + sig_data_pos_ = 0; + sig_pos_ = 0; +} + +RdataReader2::Result +RdataReader2::next() { + if (spec_pos_ < spec_count_) { + const RdataFieldSpec& spec(spec_.fields[(spec_pos_++) % + spec_.field_count]); + if (spec.type == RdataFieldSpec::DOMAIN_NAME) { + const LabelSequence sequence(data_ + data_pos_); + data_pos_ += sequence.getSerializedLength(); + return (Result(sequence, spec.name_attributes)); + } else { + const size_t length(spec.type == RdataFieldSpec::FIXEDLEN_DATA ? + spec.fixeddata_len : lengths_[length_pos_++]); + const Result result(data_ + data_pos_, length); + data_pos_ += length; + return (result); + } + } else { + sigs_ = data_ + data_pos_; + return (Result()); + } +} + +void +RdataReader2::iterate() { + while (spec_pos_ < spec_count_) { + const RdataFieldSpec& spec(spec_.fields[(spec_pos_++) % + spec_.field_count]); + if (spec.type == RdataFieldSpec::DOMAIN_NAME) { + const LabelSequence sequence(data_ + data_pos_); + data_pos_ += sequence.getSerializedLength(); + name_action_(sequence, spec.name_attributes); + } else { + const size_t length(spec.type == RdataFieldSpec::FIXEDLEN_DATA ? + spec.fixeddata_len : lengths_[length_pos_++]); + const Result result(data_ + data_pos_, length); + data_pos_ += length; + data_action_(result.data(), result.size()); + } + } + sigs_ = data_ + data_pos_; +} + +RdataReader2::Result +RdataReader2::nextSig() { + if (sig_pos_ < sig_count_) { + if (sigs_ == NULL) { + // We didn't find where the signatures start yet. We do it + // by iterating the whole data and then returning the state + // back. + const size_t data_pos = data_pos_; + const size_t spec_pos = spec_pos_; + const size_t length_pos = length_pos_; + // When the next() gets to the last item, it sets the sigs_ + while (next()) {} + assert(sigs_ != NULL); + // Return the state + data_pos_ = data_pos; + spec_pos_ = spec_pos; + length_pos_ = length_pos; + } + // Extract the result + const Result result(sigs_ + sig_data_pos_, lengths_[var_count_total_ + + sig_pos_]); + // Move the position of iterator. + sig_data_pos_ += lengths_[var_count_total_ + sig_pos_]; + ++sig_pos_; + // Call the callback + data_action_(result.data(), result.size()); + return (result); + } else { + return (Result()); + } +} + +void +RdataReader2::iterateSig() { + while (sig_pos_ < sig_count_) { + if (sigs_ == NULL) { + // We didn't find where the signatures start yet. We do it + // by iterating the whole data and then returning the state + // back. + const size_t data_pos = data_pos_; + const size_t spec_pos = spec_pos_; + const size_t length_pos = length_pos_; + // When the next() gets to the last item, it sets the sigs_ + while (next()) {} + assert(sigs_ != NULL); + // Return the state + data_pos_ = data_pos; + spec_pos_ = spec_pos; + length_pos_ = length_pos; + } + // Extract the result + const Result result(sigs_ + sig_data_pos_, lengths_[var_count_total_ + + sig_pos_]); + // Move the position of iterator. + sig_data_pos_ += lengths_[var_count_total_ + sig_pos_]; + ++sig_pos_; + // Call the callback + data_action_(result.data(), result.size()); + } +} + +size_t +RdataReader2::getSize() const { + size_t storage_size = 0; // this will be the end result + size_t data_pos = 0; + size_t length_pos = 0; + + // Go over all data fields, adding their lengths to storage_size + for (size_t spec_pos = 0; spec_pos < spec_count_; ++spec_pos) { + const RdataFieldSpec& spec = + spec_.fields[spec_pos % spec_.field_count]; + if (spec.type == RdataFieldSpec::DOMAIN_NAME) { + const size_t seq_len = + LabelSequence(data_ + data_pos).getSerializedLength(); + data_pos += seq_len; + storage_size += seq_len; + } else { + const size_t data_len = + (spec.type == RdataFieldSpec::FIXEDLEN_DATA ? + spec.fixeddata_len : lengths_[length_pos++]); + data_pos += data_len; + storage_size += data_len; + } + } + // Same for all RRSIG data + for (size_t sig_pos = 0; sig_pos < sig_count_; ++sig_pos) { + const size_t sig_data_len = lengths_[length_pos++]; + storage_size += sig_data_len; + } + + // Finally, add the size for 16-bit length fields + storage_size += (var_count_total_ * sizeof(uint16_t) + + sig_count_ * sizeof(uint16_t)); + + return (storage_size); +} + +} +} +} diff --git a/src/lib/datasrc/memory/benchmarks/rdata_reader.h b/src/lib/datasrc/memory/benchmarks/rdata_reader.h new file mode 100644 index 0000000000..f9a717bb3f --- /dev/null +++ b/src/lib/datasrc/memory/benchmarks/rdata_reader.h @@ -0,0 +1,296 @@ +// Copyright (C) 2012 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 DATASRC_MEMORY_RDATA_READER_H +#define DATASRC_MEMORY_RDATA_READER_H 1 + +#include + +#include + +#include +#include + +namespace isc { +// Some forward declarations +namespace dns{ +class RRClass; +class RRType; +} + +namespace datasrc { +namespace memory { + +/// \brief Class to read serialized rdata +/// +/// This class allows you to read the data encoded by RdataEncoder. +/// It is rather low-level -- it provides sequence of data fields +/// and names. It does not give you convenient Rdata or RRset class. +/// +/// It allows two types of operation. First one is by providing callbacks +/// to the constructor and then iterating by repeatedly calling next(), or +/// calling iterate() once. The callbacks are then called with each part of +/// the data. +/// +/// \code +/// void handleLabel(const dns::LabelSequence& label, unsigned int flags) { +/// ... +/// } +/// void handleData(const uint8_t* data, size_t size) { +/// ... +/// } +/// +/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data, +/// handleLabel, handleData); +/// reader.iterate(); +/// \endcode +/// +/// The other way is to use the return value of next() and loop through +/// it manually. It has the inconvenience of having the type condition, but +/// the code is in one place. The used way is matter of personal preferrence, +/// there's no much difference on the technical side. +/// +/// \code +/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data, +/// &handleLabel, handleData); +/// RdataReader::Result data; +/// while (data = reader.next()) { +/// switch (data.type()) { +/// case RdataReader::NAME: +/// ... +/// break; +/// case RdataReader::DATA: +/// ... +/// break; +/// default: assert(0); // Can not happen +/// } +/// } +/// \endcode +/// +/// \note It is caller's responsibility to pass valid data here. This means +/// the data returned by RdataEncoder and the corresponding class and type. +/// If this is not the case, all the kinds of pointer hell might get loose. +class RdataReader2 { +public: + /// \brief Function called on each name encountered in the data. + typedef boost::function + NameAction; + + /// \brief Function called on each data field in the data. + typedef boost::function DataAction; + + /// \brief a NameAction that does nothing. + /// + /// This is a NameAction function that does nothing. It is used + /// as a default in the constructor. + static void emptyNameAction(const dns::LabelSequence& label, + unsigned attributes); + + /// \brief a DataAction that does nothing. + /// + /// This is a DataAction function that does nothing. It is used + /// as a default in the constructor. + static void emptyDataAction(const uint8_t* data, size_t size); + + /// \brief Constructor + /// + /// This constructs the reader on top of some serialized data. + /// It does not copy the data, you have to make sure the data + /// is valid for the whole life of this object and that they + /// don't change. + /// + /// \param rrclass The class the encoded rdata belongs to. + /// \param rrtype The type of the encode rdata. + /// \param data The actual data. + /// \param rdata_count The number of Rdata encoded in the data. + /// \param sig_count The number of RRSig rdata bundled with the data. + /// \param name_action The callback to be called on each encountered name. + /// \param data_action The callback to be called on each data chunk. + RdataReader2(const dns::RRClass& rrclass, const dns::RRType& rrtype, + const uint8_t* data, size_t rdata_count, size_t sig_count, + const NameAction& name_action = &emptyNameAction, + const DataAction& data_action = &emptyDataAction); + + /// \brief The type of data returned from this iteration. + enum DataType { + NAME, ///< This iteration returns domain label + DATA, ///< This iteration returns unstructuder data + END ///< No more data to return + }; + + /// \brief Data from one iteration + /// + /// Each time you call next() or nextSig(), it returns some data. + /// This holds the data. + /// + /// It is valid only for as long as the RdataReader that returned it. + /// + /// All the methods can be called under any circumstances. However, + /// if the required property is not valid for the given type (eg. + /// when calling size() on type() == NAME), it returns some "empty" + /// value (0, NULL, or the like). + class Result { + public: + /// \brief Default constructor + /// + /// It creates an empty result (with no data) of type END. + Result() : + // TODO: Do we maybe want to have a static one to copy + // instead of constructing new one from the root Name? + label_(dns::Name::ROOT_NAME()), + data_(NULL), + size_(0), + type_(END), + compressible_(false), + additional_(false) + {} + + /// \brief Constructor from a domain label + /// + /// Creates the NAME type result. Used internally from RdataReader. + /// + /// \param label The label to hold + /// \param attributes The attributes, as stored by the serialized + /// data. + Result(const dns::LabelSequence& label, unsigned attributes); + + /// \brief Constructor from data + /// + /// Creates the DATA type result. Used internally from RdataReader. + /// + /// \param data The data pointer to hold. + /// \param size The size to hold. + Result(const uint8_t* data, size_t size); + + /// \brief The type of data returned. + DataType type() const { return (type_); } + + /// \brief The raw data. + /// + /// This is only valid if type() == DATA. + const uint8_t* data() const { return (data_); } + + /// \brief The size of the raw data. + /// + /// This is the number of bytes the data takes. It is valid only + /// if type() == DATA. + size_t size() const { return (size_); } + + /// \brief The domain label. + /// + /// This holds the domain label. It is only valid if type() == NAME. + const dns::LabelSequence& label() const { return (label_); } + + /// \brief Is the name in label() compressible? + /// + /// This is valid only if type() == NAME. + bool compressible() const { return (compressible_); } + + /// \brief Does the name expect additional processing? + /// + /// This is valid only if type() == NAME. + bool additional() const { return (additional_); } + + /// \brief If there are data returned. + /// + /// This returns if there are any data at all returned. This is + /// equivalent to action != END, but it allows for more convenient + /// code of a loop through the data. + operator bool() const { + return (type() != END); + } + private: + dns::LabelSequence label_; + const uint8_t* data_; + size_t size_; + DataType type_; + bool compressible_; + bool additional_; + }; + + /// \brief Step to next piece of data. + /// + /// This returns the next available data. Also, the apropriate hook + /// (name_action or data_action, depending on the data type) as passed + /// to the constructor is called. + /// + /// If there are no more data, a Result with type END is returned and + /// no callback is called. + Result next(); + + /// \brief Call next() until the end. + /// + /// This is just convenience method to iterate through all the data. + /// It calls next until it reaches the end (it does not rewind before, + /// therefore if you already called next() yourself, it does not start + /// at the beginning). + /// + /// The method only makes sense if you set the callbacks in constructor. + void iterate(); + + /// \brief Step to next piece of RRSig data. + /// + /// This is almost the same as next(), but it iterates through the + /// associated RRSig data, not the data for the given RRType. + Result nextSig(); + + /// \brief Iterate through all RRSig data. + /// + /// This is almost the same as iterate(), but it iterates through the + /// RRSig data instead. + void iterateSig(); + + /// \brief Rewind the iterator to the beginnig of data. + /// + /// The following next() and nextSig() will start iterating from the + /// beginning again. + void rewind(); + + /// \brief Returns the size of associated data. + /// + /// This should be the same as the return value of + /// RdataEncoder::getStorageLength() for the same set of data. + /// The intended use of this method is to tell the caller the size of + /// data that were possibly dynamically allocated so that the caller can + /// use it for deallocation. + /// + /// This method only uses the parameters given at the construction of the + /// object, and does not rely on or modify other mutable states. + /// In practice, when the caller wants to call this method, that would be + /// the only purpose of that RdataReader object (although it doesn't have + /// to be so). + size_t getSize() const; +private: + const NameAction name_action_; + const DataAction data_action_; + const RdataEncodeSpec& spec_; + // Total number of var-length fields, count of signatures + const size_t var_count_total_, sig_count_, spec_count_; + // Pointer to the beginning of length fields + const uint16_t* const lengths_; + // Pointer to the beginning of the data (after the lengths) + const uint8_t* const data_; + // Pointer to the first data signature + // Will be computed during the normal RR iteration + const uint8_t* sigs_; + // The positions in data. + size_t data_pos_, spec_pos_, length_pos_; + size_t sig_pos_, sig_data_pos_; +}; + +} +} +} + +#endif diff --git a/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc new file mode 100644 index 0000000000..24b4894fe1 --- /dev/null +++ b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc @@ -0,0 +1,334 @@ +// Copyright (C) 2012 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 + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include "rdata_reader.h" + +#include + +#include +#include + +#include + +using std::vector; +using namespace isc::bench; +using namespace isc::datasrc::memory; +using namespace isc::dns; + +namespace { +// Encapsulating parameters for a RdataReader. It extracts from the given +// RRset and its RRSIGs parameters that are necessary construct an RdataReader. +// RDATA data will be stored in the 'data' vector. +// members are defined as non cost so we can use the object of this struct +// in a vector. +struct EncodeParam { + EncodeParam(RdataEncoder& encoder, ConstRRsetPtr rrset, + ConstRRsetPtr sig_rrset = ConstRRsetPtr()) : + rrclass(rrset->getClass()), rrtype(rrset->getType()), + rdata_count(rrset->getRdataCount()), + sig_count(sig_rrset ? sig_rrset->getRdataCount() : 0) + { + encoder.start(rrclass, rrtype); + for (RdataIteratorPtr it = rrset->getRdataIterator(); + !it->isLast(); + it->next()) { + encoder.addRdata(it->getCurrent()); + } + if (sig_rrset) { + for (RdataIteratorPtr it = sig_rrset->getRdataIterator(); + !it->isLast(); + it->next()) + { + encoder.addSIGRdata(it->getCurrent()); + } + } + const size_t data_len = encoder.getStorageLength(); + data.resize(data_len); + encoder.encode(&data[0], data.size()); + } + RRClass rrclass; + RRType rrtype; + size_t rdata_count; + size_t sig_count; + vector data; +}; + +// Benchmark for the "next" type of reader. In its run() it iterates over +// the list of EncodeParams, and render the corresponding RDATA to the +// given message renderer. +class NextReaderBenchMark { +public: + NextReaderBenchMark(const vector& encode_params, + MessageRenderer& renderer) : + encode_params_(encode_params), renderer_(renderer) + {} + unsigned int run() { + vector::const_iterator it; + const vector::const_iterator it_end = + encode_params_.end(); + renderer_.clear(); + for (it = encode_params_.begin(); it != it_end; ++it) { + RdataReader2 reader(it->rrclass, it->rrtype, &it->data[0], + it->rdata_count, it->sig_count); + RdataReader2::Result data; + while ((data = reader.next())) { + switch (data.type()) { + case RdataReader2::NAME: + renderer_.writeName(data.label(), data.compressible()); + break; + case RdataReader2::DATA: + renderer_.writeData(data.data(), data.size()); + break; + default: + assert(false); + } + } + + while ((data = reader.nextSig())) { + switch (data.type()) { + case RdataReader2::DATA: + renderer_.writeData(data.data(), data.size()); + break; + default: + assert(false); + } + } + } + return (1); + } +private: + const vector& encode_params_; + MessageRenderer& renderer_; +}; + +// Similar to NextReaderBenchMark, but avoid unnecessary default construction +// of the result object and assignment operation after that. It also avoids +// relying on the type conversion operator. +class NextReaderBenchMark2 { +public: + NextReaderBenchMark2(const vector& encode_params, + MessageRenderer& renderer) : + encode_params_(encode_params), renderer_(renderer) + {} + unsigned int run() { + vector::const_iterator it; + const vector::const_iterator it_end = + encode_params_.end(); + renderer_.clear(); + for (it = encode_params_.begin(); it != it_end; ++it) { + RdataReader2 reader(it->rrclass, it->rrtype, &it->data[0], + it->rdata_count, it->sig_count); + bool reading = true; + while (reading) { + const RdataReader2::Result data = reader.next(); + switch (data.type()) { + case RdataReader2::NAME: + renderer_.writeName(data.label(), data.compressible()); + break; + case RdataReader2::DATA: + renderer_.writeData(data.data(), data.size()); + break; + case RdataReader2::END: + reading = false; + break; + } + } + + reading = true; + while (reading) { + const RdataReader2::Result data = reader.nextSig(); + switch (data.type()) { + case RdataReader2::DATA: + renderer_.writeData(data.data(), data.size()); + break; + case RdataReader2::END: + reading = false; + break; + default: + assert(false); + } + } + } + return (1); + } +private: + const vector& encode_params_; + MessageRenderer& renderer_; +}; + +// Similar to NextReaderBenchMark, but using the "iterate" type reader. +class IterateBenchMark { +public: + IterateBenchMark(const vector& encode_params, + MessageRenderer& renderer) : + encode_params_(encode_params), renderer_(renderer) + {} + unsigned int run() { + vector::const_iterator it; + const vector::const_iterator it_end = + encode_params_.end(); + renderer_.clear(); + for (it = encode_params_.begin(); it != it_end; ++it) { + RdataReader2 reader(it->rrclass, it->rrtype, &it->data[0], + it->rdata_count, it->sig_count, + boost::bind(&IterateBenchMark::renderName, + this, _1, _2), + boost::bind(&IterateBenchMark::renderData, + this, _1, _2)); + reader.iterate(); + reader.iterateSig(); + } + return (1); + } + void renderName(const LabelSequence& labels, unsigned attributes) { + const bool compress = + (attributes & NAMEATTR_COMPRESSIBLE) != 0; + renderer_.writeName(labels, compress); + } + void renderData(const uint8_t* data, size_t data_len) { + renderer_.writeData(data, data_len); + } +private: + const vector& encode_params_; + MessageRenderer& renderer_; +}; + +// Builtin benchmark data. This is a list of RDATA (of RRs) in a response +// from a root server for the query for "www.example.com" (as of this +// implementation). We use a real world example to make the case practical. +const char* const rrsets_txt = + // AUTHORITY SECTION (NS) + "com. 172800 IN NS a.gtld-servers.net.\n" + "com. 172800 IN NS b.gtld-servers.net.\n" + "com. 172800 IN NS c.gtld-servers.net.\n" + "com. 172800 IN NS d.gtld-servers.net.\n" + "com. 172800 IN NS e.gtld-servers.net.\n" + "com. 172800 IN NS f.gtld-servers.net.\n" + "com. 172800 IN NS g.gtld-servers.net.\n" + "com. 172800 IN NS h.gtld-servers.net.\n" + "com. 172800 IN NS i.gtld-servers.net.\n" + "com. 172800 IN NS j.gtld-servers.net.\n" + "com. 172800 IN NS k.gtld-servers.net.\n" + "com. 172800 IN NS l.gtld-servers.net.\n" + "com. 172800 IN NS m.gtld-servers.net.\n" + // AUTHORITY SECTION (DS) + "com. 86400 IN DS 30909 8 2 " + "E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CFC41A5766\n" + // AUTHORITY SECTION (RRSIG for DS) + "com. 86400 IN RRSIG DS 8 1 86400 20120822000000 20120814230000 50398 . " + "lcIpLRq4s91Fh1FihDXiDvVMMRqgy2jjlpiP4Y6sSjIrLue6Boi7xraj" + "Ouka34ubpl4KuIcopWe99LI/7Npvq0MYr9DaqfnX9dTW6Vc2C7/hKSsz" + "POYjraZZA3SCApgfNVzq+AscYlShi56f1vm7DQWw1eh1wHLdatidrQwNyDo=\n" + // ADDITIONAL SECTION + "a.gtld-servers.net. 172800 IN A 192.5.6.30\n" + "b.gtld-servers.net. 172800 IN A 192.33.14.30\n" + "c.gtld-servers.net. 172800 IN A 192.26.92.30\n" + "d.gtld-servers.net. 172800 IN A 192.31.80.30\n" + "e.gtld-servers.net. 172800 IN A 192.12.94.30\n" + "f.gtld-servers.net. 172800 IN A 192.35.51.30\n" + "g.gtld-servers.net. 172800 IN A 192.42.93.30\n" + "h.gtld-servers.net. 172800 IN A 192.54.112.30\n" + "i.gtld-servers.net. 172800 IN A 192.43.172.30\n" + "j.gtld-servers.net. 172800 IN A 192.48.79.30\n" + "k.gtld-servers.net. 172800 IN A 192.52.178.30\n" + "l.gtld-servers.net. 172800 IN A 192.41.162.30\n" + "m.gtld-servers.net. 172800 IN A 192.55.83.30\n" + "a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30\n" + "b.gtld-servers.net. 172800 IN AAAA 2001:503:231d::2:30"; + +void +usage() { + std::cerr << "Usage: rdata_reader_bench [-n iterations]" << std::endl; + exit (1); +} + +// Helper callback for masterLoad() used in main() to build test data. +void +setRRset(vector* rrsets, ConstRRsetPtr rrset) { + rrsets->push_back(rrset); +} +} + +int +main(int argc, char* argv[]) { + int ch; + int iteration = 100000; + while ((ch = getopt(argc, argv, "n:")) != -1) { + switch (ch) { + case 'n': + iteration = atoi(optarg); + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc != 0) { + usage(); + } + + // Build test data. rrsets will consist of a list of RRsets corresponding + // to rrsets_txt defined above. + vector rrsets; + std::stringstream rrsets_stream(rrsets_txt); + masterLoad(rrsets_stream, Name::ROOT_NAME(), RRClass::IN(), + boost::bind(setRRset, &rrsets, _1)); + + // Create EncodeParam for each RRset (possibly with RRSIG) in rrsets, + // and store them in encode_param_list. It's the direct test input. + vector encode_param_list; + RdataEncoder encoder; + encode_param_list.push_back(EncodeParam(encoder, rrsets.at(0))); + encode_param_list.push_back(EncodeParam(encoder, rrsets.at(1), + rrsets.at(2))); + for (vector::const_iterator it = rrsets.begin() + 3; + it != rrsets.end(); + ++it) { + encode_param_list.push_back(EncodeParam(encoder, *it)); + } + + // The benchmark test uses a message renderer. Create it now and keep + // using it throughout the test. + isc::util::OutputBuffer buffer(4096); // 4096 should be sufficiently large + MessageRenderer renderer; + renderer.setBuffer(&buffer); + + std::cout << "Benchmark for next-based RdataReader" << std::endl; + BenchMark(iteration, + NextReaderBenchMark(encode_param_list, + renderer)); + std::cout << "Benchmark for next-based RdataReader without type conversion" + << std::endl; + BenchMark(iteration, + NextReaderBenchMark2(encode_param_list, + renderer)); + std::cout << "Benchmark for iterator-based RdataReader" << std::endl; + BenchMark(iteration, + IterateBenchMark(encode_param_list, renderer)); + return (0); +}