Browse Source

[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.
JINMEI Tatuya 12 years ago
parent
commit
bddce14fdd

+ 1 - 0
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

+ 1 - 1
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

+ 18 - 0
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

+ 225 - 0
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<const uint16_t*>(
+             static_cast<const void*>(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);
+}
+
+}
+}
+}

+ 296 - 0
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 <datasrc/memory/rdata_field.h>
+
+#include <boost/function.hpp>
+
+#include <dns/labelsequence.h>
+#include <dns/name.h>
+
+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<void(const dns::LabelSequence&, unsigned)>
+        NameAction;
+
+    /// \brief Function called on each data field in the data.
+    typedef boost::function<void(const uint8_t*, size_t)> 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

+ 334 - 0
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 <bench/benchmark.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/masterload.h>
+
+#include <datasrc/memory/rdata_field.h>
+#include <datasrc/memory/rdata_encoder.h>
+#include "rdata_reader.h"
+
+#include <boost/bind.hpp>
+
+#include <vector>
+#include <sstream>
+
+#include <unistd.h>
+
+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<uint8_t> 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<EncodeParam>& encode_params,
+                        MessageRenderer& renderer) :
+        encode_params_(encode_params), renderer_(renderer)
+    {}
+    unsigned int run() {
+        vector<EncodeParam>::const_iterator it;
+        const vector<EncodeParam>::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<EncodeParam>& 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<EncodeParam>& encode_params,
+                         MessageRenderer& renderer) :
+        encode_params_(encode_params), renderer_(renderer)
+    {}
+    unsigned int run() {
+        vector<EncodeParam>::const_iterator it;
+        const vector<EncodeParam>::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<EncodeParam>& encode_params_;
+    MessageRenderer& renderer_;
+};
+
+// Similar to NextReaderBenchMark, but using the "iterate" type reader.
+class IterateBenchMark {
+public:
+    IterateBenchMark(const vector<EncodeParam>& encode_params,
+                     MessageRenderer& renderer) :
+        encode_params_(encode_params), renderer_(renderer)
+    {}
+    unsigned int run() {
+        vector<EncodeParam>::const_iterator it;
+        const vector<EncodeParam>::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<EncodeParam>& 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<ConstRRsetPtr>* 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<ConstRRsetPtr> 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<EncodeParam> 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<ConstRRsetPtr>::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<NextReaderBenchMark>(iteration,
+                                   NextReaderBenchMark(encode_param_list,
+                                                       renderer));
+    std::cout << "Benchmark for next-based RdataReader without type conversion"
+              << std::endl;
+    BenchMark<NextReaderBenchMark2>(iteration,
+                                    NextReaderBenchMark2(encode_param_list,
+                                                         renderer));
+    std::cout << "Benchmark for iterator-based RdataReader" << std::endl;
+    BenchMark<IterateBenchMark>(iteration,
+                                IterateBenchMark(encode_param_list, renderer));
+    return (0);
+}