initial commit
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 20 Jul 2022 07:42:54 +0000 (00:42 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 20 Jul 2022 07:42:54 +0000 (00:42 -0700)
14 files changed:
.clang-format [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.vscode/settings.json [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
COPYING.LGPLv3 [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
harness.cpp [new file with mode: 0644]
harness.h [new file with mode: 0644]
install-deps.sh [new file with mode: 0755]
main.cpp [new file with mode: 0644]
toolchain-aarch64-linux-gnu.cmake [new file with mode: 0644]
toolchain-powerpc64le-linux-gnu.cmake [new file with mode: 0644]
toolchain-x86_64-linux-gnu.cmake [new file with mode: 0644]

diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..4f236ca
--- /dev/null
@@ -0,0 +1,2 @@
+BasedOnStyle: Microsoft
+ColumnLimit: 79
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..821c2c2
--- /dev/null
@@ -0,0 +1,3 @@
+/build*
+/.cache
+/compile_commands.json
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644 (file)
index 0000000..630965c
--- /dev/null
@@ -0,0 +1,7 @@
+{
+    "cmake.buildDirectory": "${workspaceFolder}/build-x86_64",
+    "cmake.configureArgs": [
+        "-DCMAKE_TOOLCHAIN_FILE=toolchain-x86_64-linux-gnu.cmake"
+    ],
+    "cmake.copyCompileCommands": "${workspaceFolder}/compile_commands.json",
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ca7c63d
--- /dev/null
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.11.0)
+project(atomic-benchmarks VERSION 0.1.0)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+add_compile_options(-Wall -Wextra -Wimplicit-fallthrough)
+add_executable(atomic-benchmarks main.cpp harness.cpp)
+
+set(CPACK_PROJECT_NAME ${PROJECT_NAME})
+set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
+include(CPack)
diff --git a/COPYING.LGPLv3 b/COPYING.LGPLv3
new file mode 100644 (file)
index 0000000..65c5ca8
--- /dev/null
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..0c9321a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,39 @@
+.PHONY: all clean configure
+all: build-ppc64le/atomic-benchmarks build-aarch64/atomic-benchmarks build-x86_64/atomic-benchmarks
+
+common_cmake_flags = -S .
+common_cmake_flags += -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE
+
+reset_make_env = "MAKEFLAGS=" "MFLAGS=" "MAKELEVEL=" "MAKE_TERMERR=" "MAKE_TERMOUT="
+
+build-ppc64le/Makefile: toolchain-powerpc64le-linux-gnu.cmake CMakeLists.txt
+       ./install-deps.sh
+       rm -fr build-ppc64le
+       env $(reset_make_env) cmake $(common_cmake_flags) -B build-ppc64le -DCMAKE_TOOLCHAIN_FILE=toolchain-powerpc64le-linux-gnu.cmake
+
+build-aarch64/Makefile: toolchain-aarch64-linux-gnu.cmake CMakeLists.txt
+       ./install-deps.sh
+       rm -fr build-aarch64
+       env $(reset_make_env) cmake $(common_cmake_flags) -B build-aarch64 -DCMAKE_TOOLCHAIN_FILE=toolchain-aarch64-linux-gnu.cmake
+
+build-x86_64/Makefile: toolchain-x86_64-linux-gnu.cmake CMakeLists.txt
+       ./install-deps.sh
+       rm -fr build-x86_64
+       env $(reset_make_env) cmake $(common_cmake_flags) -B build-x86_64 -DCMAKE_TOOLCHAIN_FILE=toolchain-x86_64-linux-gnu.cmake
+
+configure: build-ppc64le/Makefile build-aarch64/Makefile build-x86_64/Makefile
+
+.PHONY: __force-run
+
+build-ppc64le/atomic-benchmarks: build-ppc64le/Makefile __force-run
+       $(MAKE) -C build-ppc64le atomic-benchmarks
+
+build-aarch64/atomic-benchmarks: build-aarch64/Makefile __force-run
+       $(MAKE) -C build-aarch64 atomic-benchmarks
+
+build-x86_64/atomic-benchmarks: build-x86_64/Makefile __force-run
+       $(MAKE) -C build-x86_64 atomic-benchmarks
+
+clean:
+       rm -fr build-ppc64le build-aarch64 build-x86_64
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..15518e3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# Tool for benchmarking atomic operations
+
+# Building (installs dependencies and configures cmake builds for all 3 targets):
+
+```
+make
+```
\ No newline at end of file
diff --git a/harness.cpp b/harness.cpp
new file mode 100644 (file)
index 0000000..2de66f0
--- /dev/null
@@ -0,0 +1,8 @@
+#include "harness.h"
+
+void BenchHarnessBase::base_run(
+    Config config, void (*fn)(BenchHarnessBase *bench_harness_base,
+                              std::uint64_t iteration_count))
+{
+    // FIXME: finish
+}
\ No newline at end of file
diff --git a/harness.h b/harness.h
new file mode 100644 (file)
index 0000000..3c4cd28
--- /dev/null
+++ b/harness.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <chrono>
+#include <cstdint>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+struct Config final
+{
+    std::optional<std::uint32_t> thread_count;
+    std::optional<std::uint64_t> iteration_count;
+};
+
+template <typename Fn, typename Input,
+          typename Output = std::invoke_result_t<Fn, Input>>
+class BenchHarness;
+
+class BenchHarnessBase
+{
+    template <typename Fn, typename Input, typename Output>
+    friend class BenchHarness;
+
+  private:
+    void base_run(Config config,
+                  void (*fn)(BenchHarnessBase *bench_harness_base,
+                             std::uint64_t iteration_count));
+};
+
+template <typename Fn, typename Input>
+class BenchHarness<Fn, Input, std::invoke_result_t<Fn, Input>> final
+    : private BenchHarnessBase
+{
+  private:
+    Fn fn;
+
+  public:
+    void run(Config config)
+    {
+        base_run(config, [](BenchHarnessBase *bench_harness_base,
+                            std::uint64_t iteration_count) {
+            auto &fn = static_cast<BenchHarness *>(bench_harness_base)->fn;
+            for (std::uint64_t i = 0; i < iteration_count; i++)
+            {
+                Input input;
+
+                // optimization barrier
+                asm("" : : "r"(std::addressof(input)) : "memory");
+
+                auto output = fn(input);
+
+                // optimization barrier
+                asm("" : : "r"(std::addressof(output)) : "memory");
+            }
+        });
+    }
+};
\ No newline at end of file
diff --git a/install-deps.sh b/install-deps.sh
new file mode 100755 (executable)
index 0000000..6d6084a
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+
+# need to install g++ first so the local arch will get filtered out later
+which g++ > /dev/null || (set -x; sudo apt install build-essential g++)
+
+needed=()
+
+which x86_64-linux-gnu-g++ > /dev/null || needed+=(g++-x86_64-linux-gnu)
+which aarch64-linux-gnu-g++ > /dev/null || needed+=(g++-aarch64-linux-gnu)
+which powerpc64le-linux-gnu-g++ > /dev/null || needed+=(g++-powerpc64le-linux-gnu)
+which clang++-11 > /dev/null || needed+=(clang-11)
+which make > /dev/null || needed+=(make)
+which cmake > /dev/null || needed+=(cmake)
+which ccache > /dev/null || needed+=(ccache)
+
+if ((${#needed[@]})); then
+    (set -x; sudo apt install "${needed[@]}")
+fi
diff --git a/main.cpp b/main.cpp
new file mode 100644 (file)
index 0000000..7062ec8
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,417 @@
+#include "harness.h"
+#include <charconv>
+#include <cstdlib>
+#include <functional>
+#include <initializer_list>
+#include <iostream>
+#include <map>
+#include <optional>
+#include <ostream>
+#include <string_view>
+#include <system_error>
+#include <type_traits>
+#include <vector>
+
+using namespace std::literals;
+
+enum class OptionValueKind
+{
+    None,
+    Required,
+};
+
+class OptionsParser;
+
+struct Option final
+{
+    char short_name = '\0';
+    std::string_view long_name = "", description = "";
+    bool required = false;
+    bool all_other_args_not_required = false;
+    OptionValueKind value_kind = OptionValueKind::None;
+    std::function<void(OptionsParser &parser,
+                       std::optional<std::string_view> value)>
+        parse_value;
+    bool has_short_name() const
+    {
+        return short_name != '\0';
+    }
+    bool has_long_name() const
+    {
+        return !long_name.empty();
+    }
+    friend std::ostream &operator<<(std::ostream &os, const Option &option)
+    {
+        if (option.has_long_name())
+        {
+            os << "--" << option.long_name;
+        }
+        else if (option.has_short_name())
+        {
+            os << "-" << option.short_name;
+        }
+        else
+        {
+            os << "--<unnamed>";
+        }
+        return os;
+    }
+};
+
+class Options final
+{
+    friend class OptionsParser;
+
+  private:
+    std::vector<Option> options;
+    std::map<char, std::size_t> short_options;
+    std::map<std::string_view, std::size_t> long_options;
+
+  public:
+    Options(std::initializer_list<Option> options_)
+        : options(options_), short_options(), long_options()
+    {
+        for (std::size_t i = 0; i < options.size(); i++)
+        {
+            auto &option = options[i];
+            if (option.has_short_name())
+                short_options[option.short_name] = i;
+            if (option.has_long_name())
+                long_options[option.long_name] = i;
+        }
+    }
+
+  public:
+    std::vector<std::string_view> parse(const char *const *argv) const;
+};
+
+class OptionsParser final
+{
+  private:
+    const Options &options;
+    const char *const *argv;
+    std::string_view argv0;
+    std::vector<bool> seen_options;
+    bool check_required = true;
+    std::optional<std::size_t> current_option_index;
+
+  private:
+    const Option &current_option()
+    {
+        static const Option default_option{};
+        if (current_option_index)
+        {
+            return options.options[*current_option_index];
+        }
+        else
+        {
+            return default_option;
+        }
+    }
+
+  public:
+    OptionsParser(const Options &options, const char *const *argv)
+        : options(options), argv(argv + 1),
+          argv0(argv[0] ? argv[0] : "<none>"),
+          seen_options(options.options.size(), false), current_option_index()
+    {
+        if (!argv[0])
+        {
+            static const char *null[] = {nullptr};
+            this->argv = null;
+            help_and_exit("missing program name");
+        }
+    }
+
+  private:
+    void parse_option(std::size_t option_index, std::string_view prefix,
+                      std::string_view name,
+                      std::optional<std::string_view> value)
+    {
+        auto &option = options.options[option_index];
+        switch (option.value_kind)
+        {
+        case OptionValueKind::None:
+            if (value)
+            {
+                help_and_exit("value not allowed for ", prefix, name);
+            }
+            break;
+        case OptionValueKind::Required:
+            if (!value)
+            {
+                if (*argv)
+                {
+                    value = *argv++;
+                }
+                else
+                {
+                    help_and_exit("missing value for ", prefix, name);
+                }
+            }
+            break;
+        }
+        seen_options[option_index] = true;
+        if (option.all_other_args_not_required)
+            check_required = false;
+        current_option_index = option_index;
+        option.parse_value(*this, value);
+        current_option_index = std::nullopt;
+    }
+
+  public:
+    std::vector<std::string_view> parse()
+    {
+        std::vector<std::string_view> retval;
+        constexpr auto npos = std::string_view::npos;
+        while (*argv)
+        {
+            std::string_view arg = *argv++;
+            static constexpr std::string_view long_prefix = "--"sv;
+            static constexpr std::string_view short_prefix = "-"sv;
+            if (arg.rfind(long_prefix, 0) == 0) // it starts with `--`
+            {
+                arg.remove_prefix(long_prefix.size());
+                auto eq = arg.find('=');
+                if (eq == 0)
+                {
+                    eq = npos;
+                }
+                if (arg.empty()) // just `--`
+                {
+                    while (*argv)
+                    {
+                        retval.push_back(*argv++);
+                    }
+                    break;
+                }
+                auto name = arg.substr(0, eq);
+                std::optional<std::string_view> value;
+                if (eq != npos)
+                {
+                    value = arg.substr(eq + 1);
+                }
+                auto iter = options.long_options.find(name);
+                if (iter == options.long_options.end())
+                {
+                    help_and_exit("unknown option: ", long_prefix, name);
+                }
+                auto option_index = iter->second;
+                parse_option(option_index, long_prefix, name, value);
+                continue;
+            }
+            else if (arg.rfind(short_prefix, 0) == 0) // it starts with `-`
+            {
+                arg.remove_prefix(short_prefix.size());
+                if (arg.empty()) // just `-`
+                {
+                    retval.push_back(short_prefix);
+                    continue;
+                }
+                while (!arg.empty())
+                {
+                    auto name = arg.substr(0, 1);
+                    arg.remove_prefix(1);
+                    auto iter = options.short_options.find(name[0]);
+                    if (iter == options.short_options.end())
+                    {
+                        help_and_exit("unknown option: ", short_prefix, name);
+                    }
+                    auto option_index = iter->second;
+                    std::optional<std::string_view> value;
+                    switch (options.options[option_index].value_kind)
+                    {
+                    case OptionValueKind::None:
+                        break;
+                    case OptionValueKind::Required:
+                        auto eq = arg.rfind('=', 0);
+                        if (eq != npos)
+                        {
+                            value = arg.substr(eq + 1);
+                        }
+                        else if (!arg.empty())
+                        {
+                            value = arg;
+                        }
+                        arg = "";
+                        break;
+                    }
+                    parse_option(option_index, short_prefix, name, value);
+                }
+                continue;
+            }
+            else
+            {
+                retval.push_back(arg);
+            }
+        }
+        if (check_required)
+        {
+            for (std::size_t i = 0; i < options.options.size(); i++)
+            {
+                auto &option = options.options[i];
+                if (option.required && !seen_options[i])
+                {
+                    help_and_exit("missing required option ", option);
+                }
+            }
+        }
+        return retval;
+    }
+    template <typename... Options>
+    [[noreturn]] void help_and_exit(const Options &...error_msg) const
+    {
+        auto &os = sizeof...(error_msg) == 0 ? std::cout : std::cerr;
+        if (sizeof...(error_msg) != 0)
+        {
+            ((os << "Error: ") << ... << error_msg) << std::endl;
+        }
+        os << "Usage: " << argv0;
+        for (auto &option : options.options)
+        {
+            if (!option.required)
+            {
+                os << "[";
+            }
+            auto sep = "";
+            if (option.has_short_name())
+            {
+                os << "-" << option.short_name;
+                sep = "|";
+            }
+            if (option.has_long_name())
+            {
+                os << sep << "--" << option.long_name;
+            }
+            switch (option.value_kind)
+            {
+            case OptionValueKind::None:
+                break;
+            // TODO: case OptionValueKind::Optional:
+            case OptionValueKind::Required:
+                os << " <value>";
+                break;
+            }
+            if (!option.required)
+            {
+                os << "]";
+            }
+        }
+        os << std::endl << "Options:" << std::endl;
+        for (auto &option : options.options)
+        {
+            auto sep = "";
+            if (option.has_short_name())
+            {
+                os << "-" << option.short_name;
+                sep = "|";
+            }
+            if (option.has_long_name())
+            {
+                os << sep << "--" << option.long_name;
+            }
+            os << "   " << option.description << std::endl;
+        }
+        std::exit(sizeof...(error_msg) == 0 ? 0 : 1);
+    }
+    template <typename Int>
+    std::enable_if_t<std::is_integral_v<Int>, void> parse_int(
+        std::optional<std::string_view> value, Int &i_value)
+    {
+        i_value = Int();
+        if (!value)
+        {
+            help_and_exit("missing value for ", current_option());
+        }
+        auto str = *value;
+        int base = 10;
+        if (0 == str.rfind("0x", 0) || 0 == str.rfind("0X", 0))
+        {
+            base = 16;
+            str.remove_prefix(2);
+        }
+        else if (0 == str.rfind("0o", 0) || 0 == str.rfind("0O", 0))
+        {
+            base = 8;
+            str.remove_prefix(2);
+        }
+        else if (0 == str.rfind("0b", 0) || 0 == str.rfind("0B", 0))
+        {
+            base = 2;
+            str.remove_prefix(2);
+        }
+        std::from_chars_result result = std::from_chars(
+            str.data(), str.data() + str.size(), i_value, base);
+        if (result.ptr != str.data() + str.size())
+        {
+            result.ec = std::errc::invalid_argument;
+        }
+        if (result.ec == std::errc::result_out_of_range)
+        {
+            help_and_exit("value out of range: ", current_option(), "=",
+                          *value);
+        }
+        else if (result.ec != std::errc())
+        {
+            help_and_exit("invalid value for: ", current_option());
+        }
+    }
+    template <typename Int>
+    std::enable_if_t<std::is_integral_v<Int>, void> parse_int(
+        std::optional<std::string_view> value, std::optional<Int> &i_value,
+        bool required = true)
+    {
+        if (!required && !value)
+        {
+            i_value = std::nullopt;
+            return;
+        }
+        i_value.emplace();
+        this->parse_int(value, i_value.value());
+    }
+};
+
+inline std::vector<std::string_view> Options::parse(
+    const char *const *argv) const
+{
+    return OptionsParser(*this, argv).parse();
+}
+
+int main(int, char **argv)
+{
+    Config config{};
+    Options options{
+        Option{
+            .short_name = 'h',
+            .long_name = "help",
+            .description = "Display usage and exit.",
+            .all_other_args_not_required = true,
+            .parse_value = [](OptionsParser &parser,
+                              auto) { parser.help_and_exit(); },
+        },
+        Option{.short_name = 'j',
+               .long_name = "thread-count",
+               .description = "Number of threads to run on",
+               .value_kind = OptionValueKind::Required,
+               .parse_value =
+                   [&](OptionsParser &parser, auto value) {
+                       parser.parse_int(value, config.thread_count);
+                   }},
+        Option{.short_name = 'n',
+               .long_name = "iter-count",
+               .description = "Number of iterations to run per thread",
+               .value_kind = OptionValueKind::Required,
+               .parse_value =
+                   [&](OptionsParser &parser, auto value) {
+                       parser.parse_int(value, config.iteration_count);
+                   }},
+    };
+    OptionsParser parser(options, argv);
+    auto args = parser.parse();
+    if (!args.empty())
+    {
+        parser.help_and_exit("unexpected argument");
+    }
+    // TODO: invoke benchmarks
+    return 0;
+}
diff --git a/toolchain-aarch64-linux-gnu.cmake b/toolchain-aarch64-linux-gnu.cmake
new file mode 100644 (file)
index 0000000..305ef28
--- /dev/null
@@ -0,0 +1,8 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR aarch64)
+set(CMAKE_C_COMPILER /usr/lib/ccache/aarch64-linux-gnu-gcc)
+set(CMAKE_CXX_COMPILER /usr/lib/ccache/aarch64-linux-gnu-g++)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
diff --git a/toolchain-powerpc64le-linux-gnu.cmake b/toolchain-powerpc64le-linux-gnu.cmake
new file mode 100644 (file)
index 0000000..9db2eea
--- /dev/null
@@ -0,0 +1,8 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR ppc64le)
+set(CMAKE_C_COMPILER /usr/lib/ccache/powerpc64le-linux-gnu-gcc)
+set(CMAKE_CXX_COMPILER /usr/lib/ccache/powerpc64le-linux-gnu-g++)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
diff --git a/toolchain-x86_64-linux-gnu.cmake b/toolchain-x86_64-linux-gnu.cmake
new file mode 100644 (file)
index 0000000..a5e1ee1
--- /dev/null
@@ -0,0 +1,10 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR x86_64)
+set(CMAKE_C_COMPILER /usr/lib/ccache/clang-11)
+set(CMAKE_C_COMPILER_TARGET x86_64-linux-gnu)
+set(CMAKE_CXX_COMPILER /usr/lib/ccache/clang++-11)
+set(CMAKE_CXX_COMPILER_TARGET x86_64-linux-gnu)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)