Browse Source

Adding suil C++ http framework to TechEmpower benchmarks (#6826)

* Adding suil C++ http framework to TechEmpower benchmarks

* Add whitespace

* Use better syntax when getting query arguments

* Remove LICENSE file and unused header file
Dan Carter 3 years ago
parent
commit
c2a0d8c96e

+ 39 - 0
frameworks/C++/suil/README.md

@@ -0,0 +1,39 @@
+# suil Benchmarking Test
+
+`Suil` is C++ Micro web framework which provides API's that can be used to design web applications in C++.
+- [See documentation](https://suilteam.com/) for more details
+- [Github Repo](https://github.com/dccarter/suil)
+
+### Test Type Implementation Source Code
+
+* [JSON](benchmark/src/main.cpp)
+* [PLAINTEXT](benchmark/src/main.cpp)
+* [DB](benchmark/src/main.cpp)
+* [QUERY](benchmark/src/main.cpp)
+* [UPDATE](benchmark/src/main.cpp)
+
+## Important Libraries
+The tests were run with:
+* [Software](https://www.example1.com/)
+* [Example](http://www.example2.com/)
+
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/query?queries=
+
+### UPDATE
+
+http://localhost:8080/update?queries=

+ 1 - 0
frameworks/C++/suil/benchmark/.gitattributes

@@ -0,0 +1 @@
+*.scc linguist-language=C++

+ 78 - 0
frameworks/C++/suil/benchmark/.gitignore

@@ -0,0 +1,78 @@
+
+### C template
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+### C++ template
+# Prerequisites
+
+# Compiled Object files
+*.slo
+
+# Precompiled Headers
+
+# Compiled Dynamic libraries
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+
+# Build directories
+.build
+build
+.creator
+cmake-build-*
+
+# IDE files
+.idea

+ 13 - 0
frameworks/C++/suil/benchmark/3rdParty.cmake

@@ -0,0 +1,13 @@
+set(EP_INSTALL_DIR ${CMAKE_BINARY_DIR}/deps)
+
+include(ExternalProject)
+
+set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/3rdParty)
+set(EP_PREFIX  ${CMAKE_BINARY_DIR}/3rdParty)
+
+ExternalProject_Add(catch
+        PREFIX ${EP_PREFIX}/catch
+        GIT_REPOSITORY https://github.com/catchorg/Catch2.git
+        GIT_TAG "v2.13.1"
+        CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${EP_INSTALL_DIR};-DCATCH_BUILD_EXAMPLES=OFF;-DCATCH_BUILD_TESTING=OFF;-DCATCH_INSTALL_DOCS=OFF;-DCATCH_INSTALL_HELPERS=OFF")
+set_target_properties(catch PROPERTIES EXCLUDE_FROM_ALL True)

+ 35 - 0
frameworks/C++/suil/benchmark/CMakeLists.txt

@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.15)
+project(suil-bench VERSION 0.1.0 LANGUAGES C CXX)
+
+set(SUIL_BASE_PATH "" CACHE STRING "The root path when SCF is installed")
+if (SUIL_BASE_PATH)
+    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${SUIL_BASE_PATH}/lib/cmake)
+endif()
+
+option(SUIL_BENCH_DEV "Build development binaries" OFF)
+if (SUIL_BENCH_DEV)
+    set(SUIL_BENCH_DEV 1)
+else()
+    set(SUIL_BENCH_DEV 0)
+endif()
+
+# Provides Suil* functions
+include(Suil)
+
+# Start a suil project
+SuilStartProject(suil-bench
+        EXPORTS ON
+        SCC_SOURCES src/app.scc)
+
+# Create an application target
+SuilApp(suil-bench
+    SOURCES
+        src/main.cpp
+        ${SUIL_PROJECT_SCC_PUB}/app.scc.cpp
+    LIBRARIES Suil::HttpServer
+    INSTALL   ON
+    DEPENDS   suil-bench-scc
+    DEFINES   SUIL_BENCH_DEV=${SUIL_BENCH_DEV})
+
+# Needed for installation to work
+SuilEndProject()

+ 4 - 0
frameworks/C++/suil/benchmark/cmake/Config.cmake.in

@@ -0,0 +1,4 @@
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
+check_required_components(@SUIL_PROJECT_NAME@)

+ 17 - 0
frameworks/C++/suil/benchmark/src/app.scc

@@ -0,0 +1,17 @@
+#include <suil/base/string.hpp>
+#include <suil/db/symbols.scc.hpp>
+
+#pragma load sbg
+
+namespace suil::bench {
+
+    struct [[gen::sbg(json)]] Object {
+        String message{""};
+    };
+
+    struct [[gen::sbg(json)]] World {
+        [[PRIMARY_KEY]]
+        int id{0};
+        int randomNumber{0};
+    };
+}

+ 155 - 0
frameworks/C++/suil/benchmark/src/main.cpp

@@ -0,0 +1,155 @@
+//
+// Created by Mpho Mbotho on 2021-01-15.
+//
+
+#include    <suil/base/env.hpp>
+#include <suil/http/server/pgsqlmw.hpp>
+#include <suil/http/server/endpoint.hpp>
+#include <suil/http/server/sysattrs.hpp>
+
+
+#include "app.scc.hpp"
+
+using suil::net::ServerConfig;
+using suil::net::TcpSocketConfig;
+using suil::http::server::PgSqlMiddleware;
+using suil::http::server::Endpoint;
+using suil::http::server::SystemAttrs;
+using suil::http::server::Request;
+using suil::http::server::Response;
+using suil::db::Orm;
+using suil::db::PgSqlConnection;
+using suil::bench::World;
+
+using WorldOrm = Orm<PgSqlConnection, suil::bench::World>;
+
+#define DEFAULT_POSTGRES_CONN "postgres://build:passwd@postgres:5432/dev"
+
+static inline int randnum()
+{
+    // from https://stackoverflow.com/questions/1640258/need-a-fast-random-generator-for-c
+    constexpr int MAX_VALUE = 10000;
+    static int g_seed = 0;
+    g_seed = (214013*g_seed+2531011);
+    return 1 + ((g_seed>>16)&0x7FFF)%MAX_VALUE;
+}
+
+#if SUIL_BENCH_DEV==1
+void seedDatabase(PgSqlConnection& conn)
+{
+    constexpr int MAX_RECORDS = 10000;
+    WorldOrm orm("world", conn);
+    if (orm.cifne(false)) {
+        for (int i = 0; i < MAX_RECORDS; i++) {
+            orm.insert(World{.id = i+1, .randomNumber = randnum()});
+        }
+    }
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+    suil::setup(opt(verbose, 1));
+    auto config = ServerConfig{
+        .socketConfig = TcpSocketConfig {
+            .bindAddr = {.name = "0.0.0.0", .port = 8080}
+        }
+    };
+
+    Endpoint<SystemAttrs, PgSqlMiddleware> ep{"/",
+          opt(serverConfig, std::move(config)),
+          opt(numberOfWorkers, 0)   /* Will run with number of available cores */
+    };
+
+    ep.middleware<PgSqlMiddleware>().setup(
+            suil::env("POSTGRES_CONN", DEFAULT_POSTGRES_CONN),
+            opt(ASYNC,   true),   // connections are async
+            opt(TIMEOUT, 5_sec),  // timeout on db transactions
+            opt(EXPIRES, 30_sec)  // connections are cached for 30 seconds
+    );
+
+#if SUIL_BENCH_DEV == 1
+    {
+        scoped(conn, ep.middleware<PgSqlMiddleware>().conn());
+        seedDatabase(conn);
+    }
+#endif
+
+    Route(ep, "/plaintext")
+    ("GET"_method)
+    .attrs(opt(ReplyType, "text/plain"))
+    ([]() {
+        return suil::String{"Hello, World!"};
+    });
+
+    Route(ep, "/json")
+    ("GET"_method)
+    .attrs(opt(ReplyType, "application/json"))
+    ([]() {
+        return suil::bench::Object{.message = "Hello, World!"};
+    });
+
+    Route(ep, "/db")
+    ("GET"_method)
+    .attrs(opt(ReplyType, "application/json"))
+    ([&ep]() {
+        scoped(conn, ep.middleware<PgSqlMiddleware>().conn());
+        WorldOrm orm("World", conn);
+        suil::bench::World obj{};
+        orm.find(opt(id, randnum()), obj);
+        return obj;
+    });
+
+    Route(ep, "/query")
+    ("GET"_method)
+    .attrs(opt(ReplyType, "application/json"))
+    ([&ep](const Request& req, Response& resp) {
+        auto queries = req.query().get<int>("queries") or int(1);
+        int count = std::max(1, std::min(*queries, 500));
+
+        scoped(conn, ep.context<PgSqlMiddleware>(req).conn());
+        std::vector<World> objects(count);
+        WorldOrm orm("World", conn);
+        for (auto& obj: objects) {
+            orm.find(opt(id, randnum()), obj);
+        }
+
+        resp.append(objects);
+        resp.end();
+    });
+
+    Route(ep, "/update")
+    ("GET"_method)
+    .attrs(opt(ReplyType, "application/json"))
+    ([&ep](const Request& req, Response& resp) {
+        auto queries = req.query().get<int>("queries") or int(1);
+        int count = std::max(1, std::min(*queries, 500));
+
+        scoped(conn, ep.context<PgSqlMiddleware>(req).conn());
+        std::vector<World> objects(count);
+
+        WorldOrm orm("World", conn);
+
+        for (auto& obj: objects) {
+            orm.find(opt(id, randnum()), obj);
+            obj.randomNumber = randnum();
+        }
+
+        {
+            suil::db::PgSqlTransaction txn(conn.get());
+            txn.begin();
+
+            // Sorting transactions to avoid postgres deadlock
+            std::sort(objects.begin(), objects.end(),
+                      [](const World& a, const World& b) { return a.id < b.id; });
+            for (auto& obj: objects) {
+                orm.update(obj);
+            }
+        }
+
+        resp.append(objects);
+        resp.end();
+    });
+
+    return ep.start();
+}

+ 29 - 0
frameworks/C++/suil/benchmark_config.json

@@ -0,0 +1,29 @@
+{
+  "framework": "suil",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/query?queries=",
+        "update_url": "/update?queries=",
+        "port": 8080,
+        "approach": "Stripped",
+        "classification": "Micro",
+        "database": "postgres",
+        "framework": "suil",
+        "language": "C++",
+        "flavor": "None",
+        "orm": "Micro",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "suil",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 11 - 0
frameworks/C++/suil/scripts/build.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -e
+
+# Build project
+mkdir -p .build
+cd .build
+cmake .. -DCMAKE_BUILD_TYPE=Release \
+         -DCMAKE_INSTALL_PREFIX=/install \
+         -DSUIL_BASE_PATH=$(cat /var/SUIL_BASE)
+make install

+ 22 - 0
frameworks/C++/suil/scripts/download-framework.sh

@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -e
+
+# Download's suil
+RELEASE_NAME=v${SUIL_VERSION}
+if [ -n "${SUIL_VERSION_TAG}" ] ; then
+    RELEASE_NAME=${RELEASE_NAME}-${SUIL_VERSION_TAG}
+fi
+
+echo "Fetching suil {version=${SUIL_VERSION}} artifacts"
+BASE_DIR=Suil-${SUIL_VERSION}-${SUIL_CONFIGURATION}
+BINARY=${BASE_DIR}.tar.gz
+DOWNLOAD_URL="https://github.com/dccarter/suil/releases/download/${RELEASE_NAME}/${BINARY}"
+echo "Downloading suil from ${DOWNLOAD_URL}"
+wget -q ${DOWNLOAD_URL} -O /tmp/${BINARY}
+tar -xzf /tmp/${BINARY} -C /var
+ls /var/${BASE_DIR}
+echo "Suil upacked /var/${BASE_DIR}"
+
+echo /var/$BASE_DIR > /var/SUIL_BASE
+echo $DOWNLOAD_URL  > /var/SUIL_DOWNLOAD_URL

+ 18 - 0
frameworks/C++/suil/suil.dockerfile

@@ -0,0 +1,18 @@
+FROM suilteam/base:alpine
+
+COPY ./ suil-bench
+
+ENV SUIL_VERSION=0.1.0
+ENV SUIL_VERSION_TAG=alpha
+ENV SUIL_CONFIGURATION=Release
+
+WORKDIR /suil-bench
+RUN ./scripts/download-framework.sh
+
+WORKDIR /suil-bench/benchmark
+RUN ../scripts/build.sh
+
+ENV POSTGRES_CONN="postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world"
+
+EXPOSE 8080
+CMD ["/install/bin/suil-bench"]