diff --git a/CMakeLists.txt b/CMakeLists.txt index 63d5212..74dabb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,7 @@ +cmake_minimum_required(VERSION 3.18) + +project(nvbench CUDA) + include(CPM) CPMAddPackage( @@ -23,9 +27,5 @@ CPMAddPackage( # properties...Resorting to brute force to unblock. set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAG} -std=c++17") -add_library(nvbench INTERFACE) -target_include_directories(nvbench INTERFACE "${CMAKE_CURRENT_LIST_DIR}") -target_link_libraries(nvbench INTERFACE benchmark_main) -set_target_properties(nvbench PROPERTIES INTERFACE_COMPILE_FEATURES cuda_std_17) - +add_subdirectory(nvbench) add_subdirectory(testing) diff --git a/nvbench/CMakeLists.txt b/nvbench/CMakeLists.txt new file mode 100644 index 0000000..4d14e39 --- /dev/null +++ b/nvbench/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs + axis_base.cu + int64_axis.cu +) + +# TODO shared may be a good idea to reduce compilation overhead for large +# benchmark libraries. +add_library(nvbench STATIC ${srcs}) +# TODO generator expression for installed paths +target_include_directories(nvbench PUBLIC "${nvbench_SOURCE_DIR}") +target_link_libraries(nvbench PRIVATE fmt::fmt) +target_link_libraries(nvbench INTERFACE benchmark_main) +set_target_properties(nvbench PROPERTIES COMPILE_FEATURES cuda_std_17) diff --git a/nvbench/axis_base.cu b/nvbench/axis_base.cu new file mode 100644 index 0000000..8bedec8 --- /dev/null +++ b/nvbench/axis_base.cu @@ -0,0 +1,8 @@ +#include "axis_base.cuh" + +namespace nvbench +{ + +axis_base::~axis_base() = default; + +} // namespace nvbench diff --git a/nvbench/axis_base.cuh b/nvbench/axis_base.cuh new file mode 100644 index 0000000..b638f3e --- /dev/null +++ b/nvbench/axis_base.cuh @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +namespace nvbench +{ + +enum class axis_type +{ + type, + int64, + float64, + string +}; + +struct axis_base +{ + virtual ~axis_base(); + + [[nodiscard]] const std::string &get_name() const { return m_name; } + + [[nodiscard]] axis_type get_type() const { return m_type; } + + [[nodiscard]] std::size_t get_size() const { return this->do_get_size(); } + + [[nodiscard]] std::string get_user_string(std::size_t i) const + { + return this->do_get_user_string(i); + } + + [[nodiscard]] std::string get_user_description(std::size_t i) const + { + return this->do_get_user_description(i); + } + +protected: + axis_base(std::string name, axis_type type) + : m_name{std::move(name)} + , m_type{type} + {} + +private: + virtual std::size_t do_get_size() const = 0; + virtual std::string do_get_user_string(std::size_t) const = 0; + virtual std::string do_get_user_description(std::size_t) const = 0; + + std::string m_name; + axis_type m_type; +}; + +} // namespace nvbench diff --git a/nvbench/int64_axis.cu b/nvbench/int64_axis.cu new file mode 100644 index 0000000..b083515 --- /dev/null +++ b/nvbench/int64_axis.cu @@ -0,0 +1,52 @@ +#include + +#include + +#include +#include +#include + +namespace nvbench +{ + +int64_axis::~int64_axis() = default; + +void int64_axis::set_inputs(const std::vector &inputs) +{ + m_inputs = inputs; + if (!m_is_power_of_two) + { + m_values = inputs; + } + else + { + m_values.resize(inputs.size()); + + auto conv = [](int64_t in) -> int64_t { + if (in < 0 || in >= 64) + { + throw std::runtime_error(fmt::format("{}:{}: Input value exceeds valid " + "range " + "for power-of-two mode. " + "Input={} ValidRange=[0, 63]", + __FILE__, + __LINE__, + in)); + } + return 1ll << in; + }; + + std::transform(inputs.cbegin(), inputs.cend(), m_values.begin(), conv); + } +} +std::string int64_axis::do_get_user_string(std::size_t i) const +{ + return fmt::to_string(m_inputs[i]); +} +std::string int64_axis::do_get_user_description(std::size_t i) const +{ + return m_is_power_of_two ? fmt::format("2^{} = {}", m_inputs[i], m_values[i]) + : std::string{}; +} + +} // namespace nvbench diff --git a/nvbench/int64_axis.cuh b/nvbench/int64_axis.cuh new file mode 100644 index 0000000..46a2945 --- /dev/null +++ b/nvbench/int64_axis.cuh @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include +#include + +namespace nvbench +{ + +struct int64_axis final : public axis_base +{ + int64_axis(std::string name, bool is_power_of_two) + : axis_base{std::move(name), axis_type::int64} + , m_is_power_of_two{is_power_of_two} + {} + + ~int64_axis() final; + + [[nodiscard]] bool get_is_power_of_two() const { return m_is_power_of_two; } + + void set_inputs(const std::vector &inputs); + + [[nodiscard]] const std::vector &get_inputs() const + { + return m_inputs; + }; + + [[nodiscard]] const std::vector &get_values() const + { + return m_values; + }; + + std::size_t do_get_size() const final { return m_inputs.size(); } + std::string do_get_user_string(std::size_t) const final; + std::string do_get_user_description(std::size_t) const final; + +private: + std::vector m_inputs; + std::vector m_values; + bool m_is_power_of_two; +}; + +} // diff --git a/nvbench/types.cuh b/nvbench/types.cuh new file mode 100644 index 0000000..a508201 --- /dev/null +++ b/nvbench/types.cuh @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace nvbench +{ + +using int8_t = std::int8_t; +using int16_t = std::int16_t; +using int32_t = std::int32_t; +using int64_t = std::int64_t; +using uint8_t = std::uint8_t; +using uint16_t = std::uint16_t; +using uint32_t = std::uint32_t; +using uint64_t = std::uint64_t; +using float32_t = float; +using float64_t = double; + +} // namespace nvbench diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt index 36fab06..b54d0e2 100644 --- a/testing/CMakeLists.txt +++ b/testing/CMakeLists.txt @@ -1,4 +1,5 @@ set(test_srcs + int64_axis.cu type_list.cu ) @@ -6,7 +7,7 @@ foreach(test_src IN LISTS test_srcs) get_filename_component(test_name "${test_src}" NAME_WLE) string(PREPEND test_name "nvbench.test.") add_executable(${test_name} "${test_src}") - target_link_libraries(${test_name} PRIVATE nvbench) + target_link_libraries(${test_name} PRIVATE nvbench fmt) set_target_properties(${test_name} PROPERTIES COMPILE_FEATURES cuda_std_17) add_test(NAME ${test_name} COMMAND "$") endforeach() diff --git a/testing/int64_axis.cu b/testing/int64_axis.cu new file mode 100644 index 0000000..c92c253 --- /dev/null +++ b/testing/int64_axis.cu @@ -0,0 +1,58 @@ +#include + +#include "testing/test_asserts.cuh" + +#include + +void test_basic() +{ + nvbench::int64_axis axis{"BasicAxis", false}; + ASSERT(axis.get_name() == "BasicAxis"); + ASSERT(axis.get_type() == nvbench::axis_type::int64); + ASSERT(!axis.get_is_power_of_two()); + + axis.set_inputs({0, 1, 2, 3, 7, 6, 5, 4}); + ASSERT(axis.get_size() == 8); + + std::vector ref{0, 1, 2, 3, 7, 6, 5, 4}; + ASSERT(axis.get_inputs() == ref); + ASSERT(axis.get_values() == ref); + + for (size_t i = 0; i < 8; ++i) + { + ASSERT(axis.get_user_string(i) == fmt::to_string(ref[i])); + ASSERT(axis.get_user_description(i).empty()); + } +} + +void test_power_of_two() +{ + nvbench::int64_axis axis{"POTAxis", true}; + ASSERT(axis.get_name() == "POTAxis"); + ASSERT(axis.get_type() == nvbench::axis_type::int64); + ASSERT(axis.get_is_power_of_two()); + + axis.set_inputs({0, 1, 2, 3, 7, 6, 5, 4}); + ASSERT(axis.get_size() == 8); + + std::vector ref_inputs{0, 1, 2, 3, 7, 6, 5, 4}; + std::vector ref_values{1, 2, 4, 8, 128, 64, 32, 16}; + ASSERT(axis.get_inputs() == ref_inputs); + ASSERT(axis.get_values() == ref_values); + + for (size_t i = 0; i < 8; ++i) + { + fmt::print("{}: {}\n", i, axis.get_user_description(i)); + ASSERT(axis.get_user_string(i) == fmt::to_string(ref_inputs[i])); + ASSERT(axis.get_user_description(i) == + fmt::format("2^{} = {}", ref_inputs[i], ref_values[i])); + } +} + +int main() +{ + test_basic(); + test_power_of_two(); + + return EXIT_SUCCESS; +} diff --git a/testing/test_asserts.cuh b/testing/test_asserts.cuh new file mode 100644 index 0000000..e0a8094 --- /dev/null +++ b/testing/test_asserts.cuh @@ -0,0 +1,31 @@ +#pragma once + +#include + +#define ASSERT(cond) \ + do \ + { \ + if (cond) \ + {} \ + else \ + { \ + fmt::print("{}:{}: Assertion failed ({}).\n", __FILE__, __LINE__, #cond); \ + exit(EXIT_FAILURE); \ + } \ + } while (false) + +#define ASSERT_MSG(cond, msg) \ + do \ + { \ + if (cond) \ + {} \ + else \ + { \ + fmt::print("{}:{}: Test assertion failed ({}) {}\n", \ + __FILE__, \ + __LINE__, \ + #cond, \ + msg); \ + exit(EXIT_FAILURE); \ + } \ + } while (false)