This is an automated email from the ASF dual-hosted git repository. jdanek pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git
The following commit(s) were added to refs/heads/main by this push: new fd3cf61 DISPATCH-1783 Add TCP EchoServer and Socket C++ fixtures in unittests and benchmarks (#1296) fd3cf61 is described below commit fd3cf618d99eab86a6dd12bcdf962d2026b57ec9 Author: Jiri Daněk <jda...@redhat.com> AuthorDate: Sat Jul 17 09:10:41 2021 +0200 DISPATCH-1783 Add TCP EchoServer and Socket C++ fixtures in unittests and benchmarks (#1296) --- src/adaptors/http2/http2_adaptor.c | 1 - tests/c_benchmarks/CMakeLists.txt | 8 ++ tests/c_benchmarks/Socket.cpp | 158 +++++++++++++++++++++++++++++++++ tests/c_benchmarks/Socket.hpp | 56 ++++++++++++ tests/c_benchmarks/SocketException.cpp | 45 ++++++++++ tests/c_benchmarks/SocketException.hpp | 40 +++++++++ tests/c_benchmarks/TCPServerSocket.cpp | 64 +++++++++++++ tests/c_benchmarks/TCPServerSocket.hpp | 40 +++++++++ tests/c_benchmarks/TCPSocket.cpp | 38 ++++++++ tests/c_benchmarks/TCPSocket.hpp | 41 +++++++++ tests/c_benchmarks/bm_parse_tree.cpp | 95 ++++++++++++++++++++ tests/c_benchmarks/bm_tcp_adapter.cpp | 105 ++++++++++++++++++++++ tests/c_benchmarks/echo_server.cpp | 22 +++++ tests/c_benchmarks/echo_server.hpp | 117 ++++++++++++++++++++++++ tests/c_benchmarks/socket_utils.cpp | 52 +++++++++++ tests/c_benchmarks/socket_utils.hpp | 40 +++++++++ tests/c_unittests/helpers.hpp | 49 ++++++++++ 17 files changed, 970 insertions(+), 1 deletion(-) diff --git a/src/adaptors/http2/http2_adaptor.c b/src/adaptors/http2/http2_adaptor.c index 775998b..b526a0d 100644 --- a/src/adaptors/http2/http2_adaptor.c +++ b/src/adaptors/http2/http2_adaptor.c @@ -1990,7 +1990,6 @@ static uint64_t qdr_http_deliver(void *context, qdr_link_t *link, qdr_delivery_t free_http2_stream_data(stream_data, false); } return disp; - } diff --git a/tests/c_benchmarks/CMakeLists.txt b/tests/c_benchmarks/CMakeLists.txt index 3f0f52c..b0a1895 100644 --- a/tests/c_benchmarks/CMakeLists.txt +++ b/tests/c_benchmarks/CMakeLists.txt @@ -32,6 +32,14 @@ add_executable(c-benchmarks ../c_unittests/helpers.cpp c_benchmarks_main.cpp bm_router_initialization.cpp + bm_parse_tree.cpp + bm_tcp_adapter.cpp + echo_server.cpp echo_server.hpp + socket_utils.cpp socket_utils.hpp + Socket.cpp Socket.hpp + SocketException.cpp SocketException.hpp + TCPSocket.cpp TCPSocket.hpp + TCPServerSocket.cpp TCPServerSocket.hpp $<TARGET_OBJECTS:qpid-dispatch>) target_link_libraries(c-benchmarks qpid-dispatch-libraries benchmark pthread) diff --git a/tests/c_benchmarks/Socket.cpp b/tests/c_benchmarks/Socket.cpp new file mode 100644 index 0000000..0b8637e --- /dev/null +++ b/tests/c_benchmarks/Socket.cpp @@ -0,0 +1,158 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Socket.hpp" + +#include "SocketException.hpp" +#include "socket_utils.hpp" + +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <unistd.h> + +Socket::Socket(int type, int protocol) noexcept(false) +{ + mFileDescriptor = socket(PF_INET, type, protocol); + if (mFileDescriptor < 0) { + throw SocketException("Socket creation failed (socket())", true); + } +} + +Socket::Socket(int fd) +{ + this->mFileDescriptor = fd; +} + +Socket::~Socket() +{ + if (mFileDescriptor < 0) { + return; // socket was moved out before + } + + ::close(mFileDescriptor); + mFileDescriptor = -1; +} + +std::string Socket::getLocalAddress() const +{ + sockaddr_in addr; + unsigned int addr_len = sizeof(addr); + + if (getsockname(mFileDescriptor, reinterpret_cast<sockaddr *>(&addr), &addr_len) < 0) { + throw SocketException("Fetch of local address failed (getsockname())", true); + } + return inet_ntoa(addr.sin_addr); +} + +unsigned short Socket::getLocalPort() +{ + sockaddr_in addr; + unsigned int addr_len = sizeof(addr); + + if (getsockname(mFileDescriptor, reinterpret_cast<sockaddr *>(&addr), &addr_len) < 0) { + throw SocketException("Fetch of local port failed (getsockname())", true); + } + return ntohs(addr.sin_port); +} + +void Socket::setLocalPort(unsigned short localPort) +{ + // Bind the socket to its port + sockaddr_in localAddr = {}; + localAddr.sin_family = AF_INET; + localAddr.sin_addr.s_addr = htonl(INADDR_ANY); + localAddr.sin_port = htons(localPort); + + if (bind(mFileDescriptor, reinterpret_cast<const sockaddr *>(&localAddr), sizeof(sockaddr_in)) < 0) { + throw SocketException("Set of local port failed (bind())", true); + } +} + +void Socket::setLocalAddressAndPort(const std::string &localAddress, unsigned short localPort) +{ + // Get the address of the requested host + sockaddr_in localAddr; + fillSockAddr(localAddress, localPort, localAddr); + + if (bind(mFileDescriptor, reinterpret_cast<const sockaddr *>(&localAddr), sizeof(sockaddr_in)) < 0) { + throw SocketException("Set of local address and port failed (bind())", true); + } +} + +unsigned short Socket::resolveService(const std::string &service, const std::string &protocol) +{ + struct servent *serv = getservbyname(service.c_str(), protocol.c_str()); + if (serv == nullptr) { + return atoi(service.c_str()); + } else { + return ntohs(serv->s_port); + } +} + +void Socket::connect(const std::string &remoteAddress, unsigned short remotePort) noexcept(false) +{ + sockaddr_in destAddr; + fillSockAddr(remoteAddress, remotePort, destAddr); + + if (::connect(mFileDescriptor, reinterpret_cast<const sockaddr *>(&destAddr), sizeof(destAddr)) < 0) { + throw SocketException("Connect failed (connect())", true); + } +} + +void Socket::send(const void *buffer, int bufferLen) noexcept(false) +{ + if (::send(mFileDescriptor, buffer, bufferLen, 0) < 0) { + throw SocketException("Send failed (send())", true); + } +} + +int Socket::recv(void *buffer, int bufferLen) noexcept(false) +{ + int rtn = ::recv(mFileDescriptor, buffer, bufferLen, 0); + if (rtn < 0) { + throw SocketException("Received failed (recv())", true); + } + + return rtn; +} + +std::string Socket::getRemoteAddress() noexcept(false) +{ + sockaddr_in addr; + unsigned int addr_len = sizeof(addr); + + if (getpeername(mFileDescriptor, reinterpret_cast<sockaddr *>(&addr), &addr_len) < 0) { + throw SocketException("Fetch of remote address failed (getpeername())", true); + } + return inet_ntoa(addr.sin_addr); +} + +unsigned short Socket::getRemotePort() noexcept(false) +{ + sockaddr_in addr; + unsigned int addr_len = sizeof(addr); + + if (getpeername(mFileDescriptor, reinterpret_cast<sockaddr *>(&addr), &addr_len) < 0) { + throw SocketException("Fetch of remote port failed (getpeername())", true); + } + return ntohs(addr.sin_port); +} diff --git a/tests/c_benchmarks/Socket.hpp b/tests/c_benchmarks/Socket.hpp new file mode 100644 index 0000000..f89ce04 --- /dev/null +++ b/tests/c_benchmarks/Socket.hpp @@ -0,0 +1,56 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef QPID_DISPATCH_SOCKET_HPP +#define QPID_DISPATCH_SOCKET_HPP + +#include <string> +class Socket +{ + public: + ~Socket(); + std::string getLocalAddress() const; + unsigned short getLocalPort(); + void setLocalPort(unsigned short localPort); + void setLocalAddressAndPort(const std::string &localAddress, unsigned short localPort = 0); + static unsigned short resolveService(const std::string &service, const std::string &protocol = "tcp"); + Socket(const Socket &&sock) noexcept : mFileDescriptor(sock.mFileDescriptor) + { + } + + private: + Socket(const Socket &sock); + void operator=(const Socket &sock); + + protected: + int mFileDescriptor; + Socket(int type, int protocol) noexcept(false); + explicit Socket(int fd); + + public: + void connect(const std::string &remoteAddress, unsigned short remotePort) noexcept(false); + void send(const void *buffer, int bufferLen) noexcept(false); + int recv(void *buffer, int bufferLen) noexcept(false); + std::string getRemoteAddress() noexcept(false); + unsigned short getRemotePort() noexcept(false); +}; + +#endif // QPID_DISPATCH_SOCKET_HPP diff --git a/tests/c_benchmarks/SocketException.cpp b/tests/c_benchmarks/SocketException.cpp new file mode 100644 index 0000000..db10d78 --- /dev/null +++ b/tests/c_benchmarks/SocketException.cpp @@ -0,0 +1,45 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SocketException.hpp" + +#include <cerrno> +#include <cstring> +#include <string> + +SocketException::SocketException(std::string message, bool captureErrno) noexcept : mMessage(std::move(message)) +{ + if (captureErrno) { + message.append(": "); + message.append(strerror(errno)); + } +} + +SocketException::SocketException(const SocketException &e) noexcept : mMessage(e.mMessage) +{ +} + +SocketException::~SocketException() noexcept = default; + +const char *SocketException::what() const noexcept +{ + return mMessage.c_str(); +} \ No newline at end of file diff --git a/tests/c_benchmarks/SocketException.hpp b/tests/c_benchmarks/SocketException.hpp new file mode 100644 index 0000000..7132813 --- /dev/null +++ b/tests/c_benchmarks/SocketException.hpp @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef QPID_DISPATCH_SOCKETEXCEPTION_HPP +#define QPID_DISPATCH_SOCKETEXCEPTION_HPP + +#include <string> +class SocketException : public std::exception +{ + private: + std::string mMessage; + + public: + explicit SocketException(std::string message, bool captureErrno = false) noexcept; + + SocketException(const SocketException &e) noexcept; + + ~SocketException() noexcept override; + const char *what() const noexcept override; +}; + +#endif // QPID_DISPATCH_SOCKETEXCEPTION_HPP diff --git a/tests/c_benchmarks/TCPServerSocket.cpp b/tests/c_benchmarks/TCPServerSocket.cpp new file mode 100644 index 0000000..3c98840 --- /dev/null +++ b/tests/c_benchmarks/TCPServerSocket.cpp @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "TCPServerSocket.hpp" + +#include "SocketException.hpp" + +#include <netinet/in.h> +#include <sys/socket.h> + +#include <string> + +TCPServerSocket::TCPServerSocket(unsigned short localPort, int queueLen) : Socket(SOCK_STREAM, IPPROTO_TCP) +{ + setLocalPort(localPort); + setListen(queueLen); +} + +TCPServerSocket::TCPServerSocket(const std::string &localAddress, unsigned short localPort, int queueLen) + : Socket(SOCK_STREAM, IPPROTO_TCP) +{ + setLocalAddressAndPort(localAddress, localPort); + setListen(queueLen); +} + +TCPSocket *TCPServerSocket::accept() +{ + int newConnSD = ::accept(mFileDescriptor, nullptr, nullptr); + if (newConnSD < 0) { + throw SocketException("Accept failed (accept())", true); + } + + return new TCPSocket(newConnSD); +} + +void TCPServerSocket::shutdown() +{ + ::shutdown(this->mFileDescriptor, ::SHUT_RD); +} + +void TCPServerSocket::setListen(int queueLen) +{ + if (listen(mFileDescriptor, queueLen) < 0) { + throw SocketException("Set listening socket failed (listen())", true); + } +} \ No newline at end of file diff --git a/tests/c_benchmarks/TCPServerSocket.hpp b/tests/c_benchmarks/TCPServerSocket.hpp new file mode 100644 index 0000000..eb2e465 --- /dev/null +++ b/tests/c_benchmarks/TCPServerSocket.hpp @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef QPID_DISPATCH_TCPSERVERSOCKET_HPP +#define QPID_DISPATCH_TCPSERVERSOCKET_HPP + +#include "Socket.hpp" +#include "TCPSocket.hpp" + +class TCPServerSocket : public Socket +{ + private: + void setListen(int queueLen); + + public: + TCPServerSocket(unsigned short localPort, int queueLen = 10); + TCPServerSocket(const std::string &localAddress, unsigned short localPort, int queueLen = 10); + TCPSocket *accept(); + void shutdown(); +}; + +#endif // QPID_DISPATCH_TCPSERVERSOCKET_HPP diff --git a/tests/c_benchmarks/TCPSocket.cpp b/tests/c_benchmarks/TCPSocket.cpp new file mode 100644 index 0000000..51ac9c6 --- /dev/null +++ b/tests/c_benchmarks/TCPSocket.cpp @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "TCPSocket.hpp" + +#include <netinet/in.h> +#include <sys/socket.h> + +TCPSocket::TCPSocket() : Socket(SOCK_STREAM, IPPROTO_TCP) +{ +} + +TCPSocket::TCPSocket(const std::string &remoteAddress, unsigned short remotePort) : Socket(SOCK_STREAM, IPPROTO_TCP) +{ + connect(remoteAddress, remotePort); +} + +TCPSocket::TCPSocket(int newConnSD) : Socket(newConnSD) +{ +} diff --git a/tests/c_benchmarks/TCPSocket.hpp b/tests/c_benchmarks/TCPSocket.hpp new file mode 100644 index 0000000..bffc7ce --- /dev/null +++ b/tests/c_benchmarks/TCPSocket.hpp @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef QPID_DISPATCH_TCPSOCKET_HPP +#define QPID_DISPATCH_TCPSOCKET_HPP + +#include "Socket.hpp" +class TCPSocket : public Socket +{ + private: + friend class TCPServerSocket; + explicit TCPSocket(int newConnSD); + + public: + TCPSocket(); + TCPSocket(const std::string& remoteAddress, unsigned short remotePort); + TCPSocket(TCPSocket&& socket) noexcept : TCPSocket(socket.mFileDescriptor) + { + socket.mFileDescriptor = -1; + }; +}; + +#endif // QPID_DISPATCH_TCPSOCKET_HPP diff --git a/tests/c_benchmarks/bm_parse_tree.cpp b/tests/c_benchmarks/bm_parse_tree.cpp new file mode 100644 index 0000000..3b7c9e1 --- /dev/null +++ b/tests/c_benchmarks/bm_parse_tree.cpp @@ -0,0 +1,95 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../c_unittests/helpers.hpp" + +#include <benchmark/benchmark.h> + +#include <thread> + +extern "C" { +#include "parse_tree.h" +} // extern "C" + +static void BM_AddRemoveSinglePattern(benchmark::State &state) +{ + std::thread([&state] { + QDRMinimalEnv env{}; + + qd_iterator_t *piter = qd_iterator_string("I.am.Sam", ITER_VIEW_ALL); + qd_parse_tree_t *node = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS); + void *payload; + + for (auto _ : state) { + qd_parse_tree_add_pattern(node, piter, &payload); + qd_parse_tree_remove_pattern(node, piter); + } + + qd_parse_tree_free(node); + qd_iterator_free(piter); + }).join(); +} + +BENCHMARK(BM_AddRemoveSinglePattern)->Unit(benchmark::kMicrosecond); + +static void BM_AddRemoveMultiplePatterns(benchmark::State &state) +{ + std::thread([&state] { + QDRMinimalEnv env{}; + + int batchSize = state.range(0); + std::vector<std::string> data(batchSize); + std::vector<qd_iterator_t *> piter(batchSize); + for (int i = 0; i < batchSize; ++i) { + data[i] = "I.am.Sam_" + std::to_string(i); + piter[i] = qd_iterator_string(data[i].c_str(), ITER_VIEW_ALL); + } + qd_parse_tree_t *node = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS); + const void *payload; + + for (auto _ : state) { + for (int i = 0; i < batchSize; ++i) { + qd_parse_tree_add_pattern(node, piter[i], &payload); + } + for (int i = 0; i < batchSize; ++i) { + qd_parse_tree_remove_pattern(node, piter[i]); + } + } + + qd_parse_tree_free(node); + for (int i = 0; i < batchSize; ++i) { + qd_iterator_free(piter[i]); + } + + state.SetComplexityN(batchSize); + }).join(); +} + +BENCHMARK(BM_AddRemoveMultiplePatterns) + ->Unit(benchmark::kMicrosecond) + ->Arg(1) + ->Arg(3) + ->Arg(10) + ->Arg(30) + ->Arg(100) + ->Arg(1000) + ->Arg(100000) + ->Complexity(); diff --git a/tests/c_benchmarks/bm_tcp_adapter.cpp b/tests/c_benchmarks/bm_tcp_adapter.cpp new file mode 100644 index 0000000..06a4aae --- /dev/null +++ b/tests/c_benchmarks/bm_tcp_adapter.cpp @@ -0,0 +1,105 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../c_unittests/helpers.hpp" +#include "SocketException.hpp" +#include "TCPSocket.hpp" +#include "echo_server.hpp" + +#include <benchmark/benchmark.h> + +#include <iostream> + +extern "C" { +#include "entity_cache.h" +#include "log_private.h" +#include "parse_tree.h" + +#include "qpid/dispatch.h" + +// declarations that don't have .h file +void qd_error_initialize(); +} // extern "C" + +static TCPSocket try_to_connect(const std::string &servAddress, int echoServPort) +{ + auto then = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - then < std::chrono::seconds(3)) { + try { + TCPSocket sock(servAddress, echoServPort); + return sock; + } catch (SocketException &e) { + } + } + throw std::runtime_error("Failed to connect in time"); +} + +class LatencyMeasure +{ + static const int RCVBUFSIZE = 32; + char echoBuffer[RCVBUFSIZE + 1]; // '\0' + + std::string servAddress = "127.0.0.1"; + std::string echoString = "echoString"; + int echoStringLen = echoString.length(); + + public: + inline void latencyMeasureLoop(benchmark::State &state, unsigned short echoServerPort) + { + { + TCPSocket sock = try_to_connect(servAddress, echoServerPort); + latencyMeasureSendReceive(state, sock); // run once outside benchmark to clean the pipes first + + for (auto _ : state) { + latencyMeasureSendReceive(state, sock); + } + } + } + + inline void latencyMeasureSendReceive(benchmark::State &state, TCPSocket &sock) + { + sock.send(echoString.c_str(), echoStringLen); + + int totalBytesReceived = 0; + while (totalBytesReceived < echoStringLen) { + int bytesReceived = sock.recv(echoBuffer, RCVBUFSIZE); + if (bytesReceived <= 0) { + state.SkipWithError("unable to read from socket"); + } + totalBytesReceived += bytesReceived; + echoBuffer[bytesReceived] = '\0'; + } + } +}; + +/// Measures latency between a TCP send and a receive. +/// There is only one request in flight at all times, so this is the +/// lowest conceivable latency at the most ideal condition +/// In addition, all sends are of the same (tiny) size +static void BM_TCPEchoServerLatencyWithoutQDR(benchmark::State &state) +{ + EchoServerThread est; + + LatencyMeasure lm; + lm.latencyMeasureLoop(state, est.port()); +} + +BENCHMARK(BM_TCPEchoServerLatencyWithoutQDR)->Unit(benchmark::kMillisecond); diff --git a/tests/c_benchmarks/echo_server.cpp b/tests/c_benchmarks/echo_server.cpp new file mode 100644 index 0000000..32c5268 --- /dev/null +++ b/tests/c_benchmarks/echo_server.cpp @@ -0,0 +1,22 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "echo_server.hpp" diff --git a/tests/c_benchmarks/echo_server.hpp b/tests/c_benchmarks/echo_server.hpp new file mode 100644 index 0000000..3b9c3a6 --- /dev/null +++ b/tests/c_benchmarks/echo_server.hpp @@ -0,0 +1,117 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef QPID_DISPATCH_ECHO_SERVER_HPP +#define QPID_DISPATCH_ECHO_SERVER_HPP + +#include "../c_unittests/helpers.hpp" +#include "SocketException.hpp" +#include "TCPServerSocket.hpp" + +#include <iostream> +#include <thread> +int run_echo_server(); + +void stop_echo_server(); + +const unsigned int recv_buffer_size = 32; + +class EchoServer +{ + unsigned short mPort; + TCPServerSocket servSock; + + public: + // if mPort is 0, random free port will be allocated and assigned + EchoServer(unsigned short port = 0) : mPort(port), servSock(mPort) + { + if (mPort == 0) { + mPort = servSock.getLocalPort(); + } + } + + // will handle one TCP client and then it will return + void run() + { + try { + HandleTCPClient(servSock.accept()); + } catch (SocketException &e) { + std::cerr << e.what() << std::endl; + } + } + + void stop() + { + servSock.shutdown(); + } + + unsigned short port() + { + return mPort; + } + + private: + void HandleTCPClient(TCPSocket *sock) + { + char echoBuffer[recv_buffer_size]; + int recvMsgSize; + while ((recvMsgSize = sock->recv(echoBuffer, recv_buffer_size)) > 0) { + sock->send(echoBuffer, recvMsgSize); + } + delete sock; + } +}; + +class EchoServerThread +{ + Latch portLatch; + Latch echoServerLatch; + unsigned short echoServerPort; + std::thread u; + + public: + EchoServerThread() + { + u = std::thread([this]() { + EchoServer es(0); + echoServerPort = es.port(); + portLatch.notify(); + es.run(); + echoServerLatch.wait(); + es.stop(); + }); + + portLatch.wait(); + } + + ~EchoServerThread() + { + echoServerLatch.notify(); + u.join(); + } + + unsigned short port() + { + return echoServerPort; + } +}; + +#endif // QPID_DISPATCH_ECHO_SERVER_HPP diff --git a/tests/c_benchmarks/socket_utils.cpp b/tests/c_benchmarks/socket_utils.cpp new file mode 100644 index 0000000..0f37ae9 --- /dev/null +++ b/tests/c_benchmarks/socket_utils.cpp @@ -0,0 +1,52 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "socket_utils.hpp" + +#include "SocketException.hpp" + +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#include <stdexcept> + +void fillSockAddr(const std::string &address, unsigned short port, sockaddr_in &addr) +{ + zero(addr); + + hostent *host = gethostbyname(address.c_str()); + if (host == nullptr) { + throw SocketException("Failed to resolve name (gethostbyname())"); + } + + addr.sin_port = htons(port); + addr.sin_family = host->h_addrtype; + if (host->h_addrtype == AF_INET) { + auto sin_addr = reinterpret_cast<struct in_addr *>(host->h_addr_list[0]); + addr.sin_addr.s_addr = sin_addr->s_addr; + } else if (host->h_addrtype == AF_INET6) { + throw std::invalid_argument("IPv6 addresses are not yet supported by the test"); + // auto sin_addr = reinterpret_cast<struct in6_addr *>(host->h_addr_list[0]); + } else { + throw SocketException("Name was not resolved to IPv4 (gethostbyname())"); + } +} diff --git a/tests/c_benchmarks/socket_utils.hpp b/tests/c_benchmarks/socket_utils.hpp new file mode 100644 index 0000000..666de99 --- /dev/null +++ b/tests/c_benchmarks/socket_utils.hpp @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef QPID_DISPATCH_SOCKET_UTILS_HPP +#define QPID_DISPATCH_SOCKET_UTILS_HPP + +#include <netinet/in.h> + +#include <cstring> +#include <string> + +// Saw warning to not use `= {}` to initialize socket structs; it supposedly does not work right +// due to casting and c-style polymorphism there. It's working just fine for me, though. +template <class T> +inline void zero(T &value) +{ + memset(&value, 0, sizeof(value)); +} + +void fillSockAddr(const std::string &address, unsigned short port, sockaddr_in &addr); + +#endif // QPID_DISPATCH_SOCKET_UTILS_HPP diff --git a/tests/c_unittests/helpers.hpp b/tests/c_unittests/helpers.hpp index 3c084d9..6517b79 100644 --- a/tests/c_unittests/helpers.hpp +++ b/tests/c_unittests/helpers.hpp @@ -58,6 +58,17 @@ extern "C" { void qd_router_setup_late(qd_dispatch_t *qd); } +// low-level router initialization +extern "C" { +#include "entity_cache.h" +#include "log_private.h" + +#include "qpid/dispatch.h" + +// declarations that don't have .h file +void qd_error_initialize(); +} + // backport of C++14 feature template <class T> using remove_const_t = typename std::remove_const<T>::type; @@ -232,4 +243,42 @@ class QDR }; }; +/// Synchronizes two threads. One waits at the latch, the other releases the latch. +class Latch +{ + std::mutex mut; + std::condition_variable cv; + bool opened = false; + + public: + void notify() + { + std::lock_guard<std::mutex> lock(mut); + opened = true; + cv.notify_all(); + } + void wait() + { + std::unique_lock<std::mutex> lock(mut); + cv.wait(lock, [this] { return opened; }); + } +}; + +class QDRMinimalEnv +{ + public: + QDRMinimalEnv() + { + qd_alloc_initialize(); + qd_log_initialize(); + qd_error_initialize(); + } + + ~QDRMinimalEnv() + { + qd_log_finalize(); + qd_alloc_finalize(); + } +}; + #endif // QPID_DISPATCH_HELPERS_HPP --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org