From 66f71d88887c38f7104639209846c5bdc6a9801f Mon Sep 17 00:00:00 2001 From: Mohammad Bahoosh <12122474+the-moisrex@users.noreply.github.com> Date: Sat, 7 Oct 2023 07:36:26 -0800 Subject: [PATCH] scheme::known_port + ada benchmark --- benchmarks/ada/Makefile | 30 ++++++++++++++++ benchmarks/ada/README.md | 36 +++++++++++++++++++ benchmarks/ada/ada_benchmark.cpp | 51 ++++++++++++++++++++++++++ benchmarks/ada/ada_scheme.hpp | 48 +++++++++++++++++++++++++ benchmarks/ada/webpp_scheme.hpp | 62 ++++++++++++++++++++++++++++++++ webpp/http/headers/host.hpp | 8 ++--- webpp/uri/scheme.hpp | 54 ++++++++++++++++++++++++++++ 7 files changed, 285 insertions(+), 4 deletions(-) create mode 100644 benchmarks/ada/Makefile create mode 100644 benchmarks/ada/README.md create mode 100644 benchmarks/ada/ada_benchmark.cpp create mode 100644 benchmarks/ada/ada_scheme.hpp create mode 100644 benchmarks/ada/webpp_scheme.hpp diff --git a/benchmarks/ada/Makefile b/benchmarks/ada/Makefile new file mode 100644 index 000000000..13aae3541 --- /dev/null +++ b/benchmarks/ada/Makefile @@ -0,0 +1,30 @@ +flags = -std=c++20 -isystem /usr/local/include -L/usr/local/lib -lpthread -lbenchmark_main -lbenchmark +optflags = -flto -Ofast -DNDEBUG -march=native +files = ada_benchmark.cpp + +all: gcc +.PHONY: all + +gcc: $(files) + g++ $(flags) $(optflags) $(files) + +clang: $(files) + clang++ $(flags) $(optflags) $(files) + +gcc-noopt: $(files) + g++ $(flags) $(files) + +clang-noopt: $(files) + clang++ $(flags) $(files) + +gcc-profile-generate: $(files) + g++ $(flags) $(optflags) -fprofile-generate $(files) + +clang-profile-generate: $(files) + clang++ $(flags) $(optflags) -fprofile-generate $(files) + +gcc-profile-use: $(files) + g++ $(flags) $(optflags) -fprofile-use $(files) + +clang-profile-use: $(files) + clang++ $(flags) $(optflags) -fprofile-use $(files) diff --git a/benchmarks/ada/README.md b/benchmarks/ada/README.md new file mode 100644 index 000000000..65d2891c5 --- /dev/null +++ b/benchmarks/ada/README.md @@ -0,0 +1,36 @@ +# Ada URL vs Web++'s implementation of URL + +As of writing this, ada's url parser has more features than our implementation. In an effort to better our impl, we're +going to benchmark our features against theirs. + + +GCC: + +``` +2023-10-07T07:33:24-08:00 +Running ./a.out +Run on (8 X 3176.74 MHz CPU s) +CPU Caches: + L1 Data 32 KiB (x4) + L1 Instruction 32 KiB (x4) + L2 Unified 256 KiB (x4) + L3 Unified 6144 KiB (x1) +Load Average: 2.37, 4.32, 4.17 +---------------------------------------------------------- +Benchmark Time CPU Iterations +---------------------------------------------------------- +WebppSchemePort 10.6 ns 10.5 ns 70043092 +AdaSchemePort 12.6 ns 12.5 ns 46552076 +``` + +Clang: + +``` +2023-10-07T07:32:37-08:00 +Load Average: 2.60, 4.65, 4.27 +---------------------------------------------------------- +Benchmark Time CPU Iterations +---------------------------------------------------------- +WebppSchemePort 6.24 ns 6.23 ns 115267454 +AdaSchemePort 8.74 ns 8.67 ns 83124377 +``` diff --git a/benchmarks/ada/ada_benchmark.cpp b/benchmarks/ada/ada_benchmark.cpp new file mode 100644 index 000000000..c377942da --- /dev/null +++ b/benchmarks/ada/ada_benchmark.cpp @@ -0,0 +1,51 @@ +#include "../../webpp/std/string_view.hpp" +#include "../benchmark.hpp" +#include "./ada_scheme.hpp" +#include "./webpp_scheme.hpp" + +using namespace webpp; + + +static std::vector schemes{{"one", + "two", + "three", + "http", + "five", + "https", + "https", + "ftp", + "ftps", + "wss", + "file", + "ssh", + "scheme", + "nonsense", + "", + " ", + "d;klasjfd;alsjf", + "2408372-54", + " @#$@fdsafd"}}; + +static void WebppSchemePort(benchmark::State& state) { + std::size_t i = 0; + for (auto _ : state) { + auto scheme = schemes[i++ % schemes.size()]; + benchmark::DoNotOptimize(scheme); + auto w_scheme = webpp::v1::basic_scheme{scheme}; + auto port = w_scheme.known_port(); + benchmark::DoNotOptimize(port); + } +} +BENCHMARK(WebppSchemePort); + + +static void AdaSchemePort(benchmark::State& state) { + std::size_t i = 0; + for (auto _ : state) { + auto scheme = schemes[i++ % schemes.size()]; + benchmark::DoNotOptimize(scheme); + auto port = ada::scheme::get_special_port(scheme); + benchmark::DoNotOptimize(port); + } +} +BENCHMARK(AdaSchemePort); diff --git a/benchmarks/ada/ada_scheme.hpp b/benchmarks/ada/ada_scheme.hpp new file mode 100644 index 000000000..21f8af528 --- /dev/null +++ b/benchmarks/ada/ada_scheme.hpp @@ -0,0 +1,48 @@ +// Created by moisrex on 10/7/23. +// source of this file: ada project / scheme-inl.h + +#ifndef WEBPP_ADA_SCHEME_HPP +#define WEBPP_ADA_SCHEME_HPP + +#include +#include + +namespace ada::scheme { + + /** + * @namespace ada::scheme::details + * @brief Includes the definitions for scheme specific entities + */ + namespace details { + // for use with is_special and get_special_port + // Spaces, if present, are removed from URL. + constexpr std::string_view is_special_list[] = + {"http", " ", "https", "ws", "ftp", "wss", "file", " "}; + // for use with get_special_port + constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; + } // namespace details + + constexpr bool is_special(std::string_view scheme) { + if (scheme.empty()) { + return false; + } + int hash_value = (2 * scheme.size() + (unsigned) (scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); + } + constexpr uint16_t get_special_port(std::string_view scheme) noexcept { + if (scheme.empty()) { + return 0; + } + int hash_value = (2 * scheme.size() + (unsigned) (scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return details::special_ports[hash_value]; + } else { + return 0; + } + } + +} // namespace ada::scheme + +#endif // WEBPP_ADA_SCHEME_HPP diff --git a/benchmarks/ada/webpp_scheme.hpp b/benchmarks/ada/webpp_scheme.hpp new file mode 100644 index 000000000..b7687b37f --- /dev/null +++ b/benchmarks/ada/webpp_scheme.hpp @@ -0,0 +1,62 @@ +// Created by moisrex on 10/7/23. + +#ifndef WEBPP_WEBPP_SCHEME_HPP +#define WEBPP_WEBPP_SCHEME_HPP + +#include "../../webpp/std/string.hpp" +#include "../../webpp/std/string_view.hpp" + +#include + +namespace webpp::v1 { + + template + requires(istl::String || istl::StringView) + struct basic_scheme : stl::remove_cvref_t { + using string_type = stl::remove_cvref_t; + + template + constexpr basic_scheme(T&&... args) : string_type{stl::forward(args)...} {} + + + + /** + * @brief checks if the URI is a relative reference + */ + [[nodiscard]] constexpr bool is_relative_reference() const noexcept { + return this->empty(); + } + + /** + * @return 0 if unknown, otherwise return the port + */ + [[nodiscard]] constexpr stl::uint16_t known_port() const noexcept { + // NOLINTBEGIN(*-avoid-magic-numbers) + switch (this->size()) { + case 2: + if (this->operator[](0) == 'w' && this->operator[](1) == 's') + return 80u; + break; + case 3: + if (*this == "wss") + return 443u; + else if (*this == "ftp") + return 21; + break; + case 4: + if (*this == "http") + return 80u; + break; + case 5: + if (*this == "https") + return 443; + break; + } + return 0u; + // NOLINTEND(*-avoid-magic-numbers) + } + }; + +} // namespace webpp::v1 + +#endif // WEBPP_WEBPP_SCHEME_HPP diff --git a/webpp/http/headers/host.hpp b/webpp/http/headers/host.hpp index f965179d4..8ea654762 100644 --- a/webpp/http/headers/host.hpp +++ b/webpp/http/headers/host.hpp @@ -108,7 +108,7 @@ namespace webpp::http { // first try to parse it as ipv4: ipv4_octets octets; auto host_ptr = hostname.data(); - auto const host_end = host_ptr + hostname.size(); + auto const host_end = host_ptr + hostname.size(); // NOLINT(*-pro-bounds-pointer-arithmetic) auto const res = inet_pton4(host_ptr, host_end, octets.data()); switch (res) { using enum inet_pton4_status; @@ -304,7 +304,7 @@ namespace webpp::http { if (port_ptr == port_end) { return; // no port here } - if (*port_ptr++ != ':') { + if (*port_ptr++ != ':') { // NOLINT(*-pro-bounds-pointer-arithmetic) status_code = host_status::invalid_host; return; } @@ -354,8 +354,8 @@ namespace webpp::http { using endpoint_variant_type = stl::variant; endpoint_variant_type endpoint{stl::monostate{}}; - stl::uint16_t port_value = 0; - host_status status_code; + stl::uint16_t port_value = 0; + host_status status_code = host_status::invalid_host; }; } // namespace webpp::http diff --git a/webpp/uri/scheme.hpp b/webpp/uri/scheme.hpp index 739f819ac..22146ffc1 100644 --- a/webpp/uri/scheme.hpp +++ b/webpp/uri/scheme.hpp @@ -4,9 +4,30 @@ #define WEBPP_SCHEME_HPP #include "../std/string.hpp" +#include "./details/uri_status.hpp" namespace webpp::uri { + enum struct scheme_status { +#define webpp_def(status) status = stl::to_underlying(uri::uri_status::status) + webpp_def(valid), // valid scheme +#undef webpp_def + }; + + /** + * Get the error message as a string view + */ + static constexpr stl::string_view to_string(scheme_status status) noexcept { + switch (status) { + using enum scheme_status; + case valid: return "Valid Scheme"; + } + stl::unreachable(); + } + + + + template struct basic_scheme : stl::remove_cvref_t { using string_type = stl::remove_cvref_t; @@ -23,6 +44,39 @@ namespace webpp::uri { return this->empty(); } + /** + * @return 0 if unknown, otherwise return the port + */ + [[nodiscard]] constexpr stl::uint16_t known_port() const noexcept { + // NOLINTBEGIN(*-avoid-magic-numbers) + switch (this->size()) { + case 2: + if (this->operator[](0) == 'w' && this->operator[](1) == 's') + return 80u; + break; + case 3: + if (*this == "wss") + return 443u; + else if (*this == "ftp") + return 21; + break; + case 4: + if (*this == "http") + return 80u; + break; + case 5: + if (*this == "https") + return 443; + break; + } + return 0u; + // NOLINTEND(*-avoid-magic-numbers) + } + + [[nodiscard]] constexpr bool is_known() const noexcept { + return known_port() != 0u; + } + void append_to(istl::String auto& out) const { if (!this->empty()) { // out.reserve(out.size() + this->size() + 1);