tests: face system benchmark
Change-Id: Ia2d9125486c57f949e5c855ae6b8d95ffd5aa6e3
Refs: #3567
diff --git a/core/extended-error-message.hpp b/core/extended-error-message.hpp
index 10e62e2..f1443bd 100644
--- a/core/extended-error-message.hpp
+++ b/core/extended-error-message.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -27,6 +27,7 @@
#define NFD_CORE_EXTENDED_ERROR_MESSAGE_HPP
#include <boost/exception/get_error_info.hpp>
+#include <sstream>
namespace nfd {
diff --git a/tests/other/face-benchmark.conf.sample b/tests/other/face-benchmark.conf.sample
new file mode 100644
index 0000000..3048341
--- /dev/null
+++ b/tests/other/face-benchmark.conf.sample
@@ -0,0 +1,2 @@
+udp4://192.0.2.2:6363 udp4://192.0.2.3:6363
+tcp4://192.0.2.4:6363 tcp4://192.0.2.5:6363
diff --git a/tests/other/face-benchmark.cpp b/tests/other/face-benchmark.cpp
new file mode 100644
index 0000000..afd4cf7
--- /dev/null
+++ b/tests/other/face-benchmark.cpp
@@ -0,0 +1,190 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "core/extended-error-message.hpp"
+#include "core/global-io.hpp"
+#include "face/face.hpp"
+#include "face/tcp-channel.hpp"
+#include "face/udp-channel.hpp"
+
+#include <fstream>
+#include <iostream>
+
+namespace nfd {
+namespace tests {
+
+class FaceBenchmark
+{
+public:
+ FaceBenchmark(const char* configFileName)
+ : m_terminationSignalSet{getGlobalIoService()}
+ , m_tcpChannel{tcp::Endpoint{boost::asio::ip::tcp::v4(), 6363}}
+ , m_udpChannel{udp::Endpoint{boost::asio::ip::udp::v4(), 6363}, time::minutes{10}}
+ {
+ m_terminationSignalSet.add(SIGINT);
+ m_terminationSignalSet.add(SIGTERM);
+ m_terminationSignalSet.async_wait(bind(&FaceBenchmark::terminate, _1, _2));
+
+ parseConfig(configFileName);
+
+ m_tcpChannel.listen(bind(&FaceBenchmark::onLeftFaceCreated, this, _1),
+ bind(&FaceBenchmark::onFaceCreationFailed, _1));
+ std::clog << "Listening on " << m_tcpChannel.getUri() << std::endl;
+
+ m_udpChannel.listen(bind(&FaceBenchmark::onLeftFaceCreated, this, _1),
+ bind(&FaceBenchmark::onFaceCreationFailed, _1));
+ std::clog << "Listening on " << m_udpChannel.getUri() << std::endl;
+ }
+
+private:
+ static void
+ terminate(const boost::system::error_code& error, int signalNo)
+ {
+ if (error)
+ return;
+ getGlobalIoService().stop();
+ }
+
+ void
+ parseConfig(const char* configFileName)
+ {
+ std::ifstream file{configFileName};
+ std::string uriStrL;
+ std::string uriStrR;
+
+ while (file >> uriStrL >> uriStrR) {
+ FaceUri uriL{uriStrL};
+ FaceUri uriR{uriStrR};
+
+ if (uriL.getScheme() != "tcp4" && uriL.getScheme() != "udp4") {
+ std::clog << "Unsupported protocol '" << uriL.getScheme() << "'" << std::endl;
+ }
+ else if (uriR.getScheme() != "tcp4" && uriR.getScheme() != "udp4") {
+ std::clog << "Unsupported protocol '" << uriR.getScheme() << "'" << std::endl;
+ }
+ else {
+ m_faceUris.push_back(std::make_pair(uriL, uriR));
+ }
+ }
+
+ if (m_faceUris.empty()) {
+ BOOST_THROW_EXCEPTION(std::runtime_error("No supported FaceUri pairs found in config file"));
+ }
+ }
+
+ void
+ onLeftFaceCreated(const shared_ptr<Face>& faceL)
+ {
+ std::clog << "Left face created: remote=" << faceL->getRemoteUri()
+ << " local=" << faceL->getLocalUri() << std::endl;
+
+ // find a matching right uri
+ FaceUri uriR;
+ for (const auto& pair : m_faceUris) {
+ if (pair.first.getHost() == faceL->getRemoteUri().getHost() &&
+ pair.first.getScheme() == faceL->getRemoteUri().getScheme()) {
+ uriR = pair.second;
+ }
+ else if (pair.second.getHost() == faceL->getRemoteUri().getHost() &&
+ pair.second.getScheme() == faceL->getRemoteUri().getScheme()) {
+ uriR = pair.first;
+ }
+ }
+
+ if (uriR == FaceUri()) {
+ std::clog << "No FaceUri matched, ignoring..." << std::endl;
+ faceL->close();
+ return;
+ }
+
+ // create the right face
+ auto addr = boost::asio::ip::address::from_string(uriR.getHost());
+ auto port = boost::lexical_cast<uint16_t>(uriR.getPort());
+ if (uriR.getScheme() == "tcp4") {
+ m_tcpChannel.connect(tcp::Endpoint(addr, port),
+ bind(&FaceBenchmark::onRightFaceCreated, this, faceL, _1),
+ bind(&FaceBenchmark::onFaceCreationFailed, _1));
+ }
+ else if (uriR.getScheme() == "udp4") {
+ m_udpChannel.connect(udp::Endpoint(addr, port),
+ ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+ bind(&FaceBenchmark::onRightFaceCreated, this, faceL, _1),
+ bind(&FaceBenchmark::onFaceCreationFailed, _1));
+ }
+ }
+
+ void
+ onRightFaceCreated(const shared_ptr<Face>& faceL, const shared_ptr<Face>& faceR)
+ {
+ std::clog << "Right face created: remote=" << faceR->getRemoteUri()
+ << " local=" << faceR->getLocalUri() << std::endl;
+
+ tieFaces(faceR, faceL);
+ tieFaces(faceL, faceR);
+ }
+
+ static void
+ tieFaces(const shared_ptr<Face>& face1, const shared_ptr<Face>& face2)
+ {
+ face1->afterReceiveInterest.connect([face2] (const Interest& interest) { face2->sendInterest(interest); });
+ face1->afterReceiveData.connect([face2] (const Data& data) { face2->sendData(data); });
+ face1->afterReceiveNack.connect([face2] (const ndn::lp::Nack& nack) { face2->sendNack(nack); });
+ }
+
+ static void
+ onFaceCreationFailed(const std::string& reason)
+ {
+ BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create face: " + reason));
+ }
+
+private:
+ boost::asio::signal_set m_terminationSignalSet;
+ TcpChannel m_tcpChannel;
+ UdpChannel m_udpChannel;
+ std::vector<std::pair<FaceUri, FaceUri>> m_faceUris;
+};
+
+} // namespace tests
+} // namespace nfd
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " <config-file>" << std::endl;
+ return 2;
+ }
+
+ try {
+ nfd::tests::FaceBenchmark bench{argv[1]};
+ nfd::getGlobalIoService().run();
+ }
+ catch (const std::exception& e) {
+ std::cerr << "FATAL: " << nfd::getExtendedErrorMessage(e) << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/other/face-benchmark.md b/tests/other/face-benchmark.md
new file mode 100644
index 0000000..ae91432
--- /dev/null
+++ b/tests/other/face-benchmark.md
@@ -0,0 +1,23 @@
+# Face Benchmark
+
+**face-benchmark** is a program to test the performance of the face system. It runs
+on a node between n consumer nodes and n producer nodes. It creates a left face and
+a right face for each pair of consumer node and producer node respectively. The left
+and right faces in each pair are tied to each other at the Interest/Data/Nack layer,
+which means that anything received on the left face is sent to the corresponding
+right face, and anything received on the right face is sent to the corresponding
+left face. The program passively waits for an incoming connection from one side which
+triggers an outgoing connection to the other side. The FacePersistency is set to
+"on-demand" for all incoming faces, and "persistent" for all outgoing faces.
+
+The FaceUris for each face pair can be configured via a configuration file. Each
+line of the configuration file consists of a left FaceUri and a right FaceUri
+separated by a space. FaceUri schemes "tcp4" and "udp4" are supported. The left face
+and right face are allowed to have different FaceUri schemes. All FaceUris MUST be
+in canonical form.
+
+Usage example:
+
+1. Configure FaceUris in `face-benchmark.conf`
+2. On the router node, run `./face-benchmark face-benchmark.conf`
+3. Run NFD on the consumer/producer node pairs
diff --git a/tests/other/wscript b/tests/other/wscript
index 95fd9c5..926ba9d 100644
--- a/tests/other/wscript
+++ b/tests/other/wscript
@@ -1,7 +1,7 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
"""
-Copyright (c) 2014-2015, Regents of the University of California,
+Copyright (c) 2014-2016, Regents of the University of California,
Arizona Board of Regents,
Colorado State University,
University Pierre & Marie Curie, Sorbonne University,
@@ -27,27 +27,32 @@
top = '../..'
def build(bld):
- testPrograms = {
- "cs-benchmark": "CS Benchmark",
- "pit-fib-benchmark": "PIT & FIB Benchmark",
- }
- for module, name in testPrograms.items():
- # main()
- bld(target='unit-tests-%s-main' % module,
- name='unit-tests-%s-main' % module,
- features='cxx',
- use='BOOST',
- source='../main.cpp',
- defines=['BOOST_TEST_MODULE=%s' % name]
- )
+ for module, name in {"cs-benchmark": "CS Benchmark",
+ "pit-fib-benchmark": "PIT & FIB Benchmark"}.items():
+ # main
+ bld(target='unit-tests-%s-main' % module,
+ name='unit-tests-%s-main' % module,
+ features='cxx',
+ source='../main.cpp',
+ use='BOOST',
+ defines=['BOOST_TEST_MODULE=%s' % name]
+ )
- # unit-tests-%module
- bld.program(
- target='../../%s' % module,
- features='cxx cxxprogram',
- source=bld.path.ant_glob(['%s*.cpp' % module]),
- use='daemon-objects unit-tests-base unit-tests-%s-main' % module,
- includes='.',
- install_path=None,
- defines='UNIT_TEST_CONFIG_PATH=\"%s/tmp-files/\"' % bld.bldnode,
- )
+ # module
+ bld.program(target='../../%s' % module,
+ features='cxx cxxprogram',
+ source=bld.path.ant_glob(['%s*.cpp' % module]),
+ use='daemon-objects unit-tests-base unit-tests-%s-main' % module,
+ includes='.',
+ install_path=None,
+ defines=['UNIT_TEST_CONFIG_PATH=\"%s/tmp-files/\"' % bld.bldnode]
+ )
+
+ # face-benchmark does not rely on Boost.Test
+ bld.program(target='../../face-benchmark',
+ features='cxx cxxprogram',
+ source=bld.path.ant_glob(['face-benchmark*.cpp']),
+ use='daemon-objects',
+ includes='.',
+ install_path=None
+ )