From df20143364815cf8707c586d24e15a44ad696fee Mon Sep 17 00:00:00 2001 From: Victor Moncada Date: Thu, 27 Apr 2023 11:49:04 +0200 Subject: [PATCH] Added format module benchmark. Updated documentation. --- benchs/CMakeLists.txt | 1 + benchs/bench_format.cpp | 83 ++++++++++++++++++++++++++++++ docs/format.md | 62 +++++++++++++--------- seq/bits.hpp | 6 +++ seq/format.hpp | 110 ++++++++++++++++++++-------------------- tests/test_format.cpp | 1 + 6 files changed, 183 insertions(+), 80 deletions(-) create mode 100644 benchs/bench_format.cpp diff --git a/benchs/CMakeLists.txt b/benchs/CMakeLists.txt index f208fff6..e85b548d 100644 --- a/benchs/CMakeLists.txt +++ b/benchs/CMakeLists.txt @@ -8,6 +8,7 @@ enable_testing() # create the testing file and list of tests create_test_sourcelist (Benchs seq_benchs.cpp + bench_format.cpp bench_hash.cpp bench_mem_pool.cpp bench_map.cpp diff --git a/benchs/bench_format.cpp b/benchs/bench_format.cpp new file mode 100644 index 00000000..439450d6 --- /dev/null +++ b/benchs/bench_format.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +#ifdef SEQ_HAS_CPP_20 +#include +#endif + +int bench_format(int, char ** const) +{ + + using namespace seq; + + // Generate 4M double values + using ftype = double; + random_float_genertor rgn; + std::vector vec_d; + for (int i = 0; i < 4000000; ++i) + vec_d.push_back(rgn()); + + // Null ostream object + nullbuf n; + std::ostream oss(&n); + oss.sync_with_stdio(false); + + // Build a table of 4 * 1000000 double values separated by a '|'. All values are centered on a 20 characters space + tick(); + oss << std::setprecision(6); + for (size_t i = 0; i < vec_d.size() / 4; ++i) + { + oss << std::left << std::setw(20) << vec_d[i * 4] << "|"; + oss << std::left << std::setw(20) << vec_d[i * 4 + 1] << "|"; + oss << std::left << std::setw(20) << vec_d[i * 4 + 2] << "|"; + oss << std::left << std::setw(20) << vec_d[i * 4 + 3] << "|"; + oss << std::endl; + } + size_t el = tock_ms(); + std::cout << "Write table with streams: " << el << " ms" << std::endl; + + + // Build the same table with format module + + // Create the format object + auto slot = _g().p(6).l(20); // floating point slot with a precision of 6 and left-aligned on a 20 characters width + auto f = join("|",slot, slot, slot, slot, ""); + tick(); + for (size_t i = 0; i < vec_d.size() / 4; ++i) + oss << f(vec_d[i * 4], vec_d[i * 4 + 1], vec_d[i * 4 + 2], vec_d[i * 4 + 3]) << std::endl; + el = tock_ms(); + std::cout << "Write table with seq formatting module: " << el << " ms" << std::endl; + + + // Compare to std::format for C++20 compilers +#ifdef SEQ_HAS_CPP_20 + tick(); + for (size_t i = 0; i < vec_d.size() / 4; ++i) + std::format_to( + std::ostreambuf_iterator(oss), + "{:^20.6g} | {:^20.6g} | {:^20.6g} | {:^20.6g}\n", + vec_d[i * 4], vec_d[i * 4 + 1], vec_d[i * 4 + 2], vec_d[i * 4 + 3]); + el = tock_ms(); + std::cout << "Write table with std::format : " << el << " ms" << std::endl; +#endif + + // Just for comparison, directly dump the double values without the '|' character (but keeping alignment) + + tick(); + auto f2 = g().l(20); + for (size_t i = 0; i < vec_d.size(); ++i) + oss << f2(vec_d[i]); + el = tock_ms(); + std::cout << "Write left-aligned double with seq::fmt: " << el << " ms" << std::endl; + + + + // use std::ostream::bad() to make sure the above tests are not simply ignored by the compiler + if (oss.bad()) + std::cout << "error" << std::endl; + + + return 0; +} \ No newline at end of file diff --git a/docs/format.md b/docs/format.md index 76ab6e40..676b85ba 100644 --- a/docs/format.md +++ b/docs/format.md @@ -648,14 +648,19 @@ The following code is a simple benchmark on writing a 4 * 1000000 table of doubl #include #include +#ifdef SEQ_HAS_CPP_20 +#include +#endif + + int main(int argc, char ** argv) { using namespace seq; // Generate 4M double values - using float_type = double; - random_float_genertor rgn; - std::vector vec_d; + using ftype = double; + random_float_genertor rgn; + std::vector vec_d; for (int i = 0; i < 4000000; ++i) vec_d.push_back(rgn()); @@ -667,50 +672,55 @@ int main(int argc, char ** argv) // Build a table of 4 * 1000000 double values separated by a '|'. All values are centered on a 20 characters space tick(); oss << std::setprecision(6); - for (size_t i = 0; i < vec_d.size()/4; ++i) + for (size_t i = 0; i < vec_d.size() / 4; ++i) { oss << std::left << std::setw(20) << vec_d[i * 4] << "|"; - oss << std::left << std::setw(20) << vec_d[i * 4+1] << "|"; - oss << std::left << std::setw(20) << vec_d[i * 4+2] << "|"; - oss << std::left << std::setw(20) << vec_d[i * 4+3] << "|"; + oss << std::left << std::setw(20) << vec_d[i * 4 + 1] << "|"; + oss << std::left << std::setw(20) << vec_d[i * 4 + 2] << "|"; + oss << std::left << std::setw(20) << vec_d[i * 4 + 3] << "|"; oss << std::endl; } size_t el = tock_ms(); - std::cout << "Write table with streams: " <(), g().p(6).c(20), "|", g().p(6).c(20), "|", g().p(6).c(20), "|", g().p(6).c(20), "|"); + auto slot = _g().p(6).l(20); // floating point slot with a precision of 6 and left-aligned on a 20 characters width + auto f = join("|",slot, slot, slot, slot, ""); tick(); for (size_t i = 0; i < vec_d.size() / 4; ++i) - oss << f(vec_d[i * 4], vec_d[i * 4+1], vec_d[i * 4+2], vec_d[i * 4+3]) << std::endl; + oss << f(vec_d[i * 4], vec_d[i * 4 + 1], vec_d[i * 4 + 2], vec_d[i * 4 + 3]) << std::endl; el = tock_ms(); std::cout << "Write table with seq formatting module: " << el << " ms" << std::endl; // Compare to std::format for C++20 compilers - // tick(); - // for (size_t i = 0; i < vec_d.size() / 4; ++i) - // std::format_to(std::ostreambuf_iterator(oss), "{:^20.6g} | {:^20.6g} | {:^20.6g} | {:^20.6g}\n", vec_d[i * 4], vec_d[i * 4 + 1], vec_d[i * 4 + 2], vec_d[i * 4 + 3]); - // el = tock_ms(); - // std::cout << "Write table with std::format : " << el << " ms" << std::endl; - +#ifdef SEQ_HAS_CPP_20 + tick(); + for (size_t i = 0; i < vec_d.size() / 4; ++i) + std::format_to( + std::ostreambuf_iterator(oss), + "{:^20.6g} | {:^20.6g} | {:^20.6g} | {:^20.6g}\n", + vec_d[i * 4], vec_d[i * 4 + 1], vec_d[i * 4 + 2], vec_d[i * 4 + 3]); + el = tock_ms(); + std::cout << "Write table with std::format : " << el << " ms" << std::endl; +#endif - // Just for comparison, directly dump the double values without the '|' character (but keeping centering) + // Just for comparison, directly dump the double values without the '|' character (but keeping alignment) tick(); - auto f2 = seq::fmt(float_type(), 'g').c(20); + auto f2 = g().l(20); for (size_t i = 0; i < vec_d.size(); ++i) oss << f2(vec_d[i]); el = tock_ms(); - std::cout << "Write centered double with seq::fmt: " << el << " ms" << std::endl; + std::cout << "Write left-aligned double with seq::fmt: " << el << " ms" << std::endl; + - // use std::ostream::bad() to make sure the above tests are not simply ignored by the compiler - if ((int)oss.bad()) + if (oss.bad()) std::cout << "error" << std::endl; @@ -719,10 +729,12 @@ int main(int argc, char ** argv) ``` -Above example compiled with gcc 10.1.0 (-O3) for msys2 on Windows 10 on a Intel(R) Core(TM) i7-10850H at 2.70GHz gives the following output: +Above example compiled with msvc 14.20 (all optimization flags, C++20 support) on Windows 10 on a Intel(R) Core(TM) i7-10850H at 2.70GHz gives the following output: -> Write table with streams: 3469 ms +> Write table with streams: 4482 ms +> +> Write table with seq formatting module: 677 ms > -> Write table with seq formatting module: 413 ms +> Write table with std::format : 1107 ms > -> Write centered double with seq::fmt: 366 ms +> Write left-aligned double with seq::fmt: 519 ms diff --git a/seq/bits.hpp b/seq/bits.hpp index 66cd71d1..c0c68293 100644 --- a/seq/bits.hpp +++ b/seq/bits.hpp @@ -238,10 +238,16 @@ static constexpr void* __dummy_ptr_with_long_name = nullptr; #if _MSVC_LANG >= 201703L #define SEQ_HAS_CPP_17 #endif + #if _MSVC_LANG >= 202002L + #define SEQ_HAS_CPP_20 + #endif #else #if __cplusplus >= 201703L #define SEQ_HAS_CPP_17 #endif + #if __cplusplus >= 202002L + #define SEQ_HAS_CPP_20 + #endif #endif // If constexpr diff --git a/seq/format.hpp b/seq/format.hpp index e4406184..ef4da41b 100644 --- a/seq/format.hpp +++ b/seq/format.hpp @@ -2384,102 +2384,102 @@ namespace seq - static inline auto fmt(float value, char format) -> ostream_format + inline auto fmt(float value, char format) -> ostream_format { // Floating point formatting return ostream_format(value, format); } - static inline auto _fmt(float value, char format) -> ostream_format + inline auto _fmt(float value, char format) -> ostream_format { // Floating point formatting return ostream_format(value, format); } - static inline auto fmt(double value, char format) -> ostream_format + inline auto fmt(double value, char format) -> ostream_format { // Floating point formatting return ostream_format(value, format); } - static inline auto _fmt(double value, char format) -> ostream_format + inline auto _fmt(double value, char format) -> ostream_format { // Floating point formatting return ostream_format(value, format); } - static inline auto fmt(long double value, char format) -> ostream_format + inline auto fmt(long double value, char format) -> ostream_format { // Floating point formatting return ostream_format(value, format); } - static inline auto _fmt(long double value, char format) -> ostream_format + inline auto _fmt(long double value, char format) -> ostream_format { // Floating point formatting return ostream_format(value, format); } - static inline auto fmt(const char* str) -> ostream_format + inline auto fmt(const char* str) -> ostream_format { // String formatting return ostream_format(tstring_view(str)); } - static inline auto _fmt(const char* str) -> ostream_format + inline auto _fmt(const char* str) -> ostream_format { // String formatting return ostream_format(tstring_view(str)); } - static inline auto fmt(const char* str, size_t size) -> ostream_format + inline auto fmt(const char* str, size_t size) -> ostream_format { return ostream_format(tstring_view(str, size)); } - static inline auto _fmt(const char* str, size_t size) -> ostream_format + inline auto _fmt(const char* str, size_t size) -> ostream_format { return ostream_format(tstring_view(str, size)); } - static inline auto fmt(const std::string& str) -> ostream_format + inline auto fmt(const std::string& str) -> ostream_format { // String formatting return ostream_format(tstring_view(str.data(), str.size())); } - static inline auto _fmt(const std::string& str) -> ostream_format + inline auto _fmt(const std::string& str) -> ostream_format { // String formatting return ostream_format(tstring_view(str.data(), str.size())); } template - static inline auto fmt(const tiny_string& str) -> ostream_format + inline auto fmt(const tiny_string& str) -> ostream_format { // String formatting return ostream_format(tstring_view(str.data(), str.size())); } template - static inline auto _fmt(const tiny_string& str) -> ostream_format + inline auto _fmt(const tiny_string& str) -> ostream_format { // String formatting return ostream_format(tstring_view(str.data(), str.size())); } - static inline auto fmt(const tstring_view& str) -> ostream_format + inline auto fmt(const tstring_view& str) -> ostream_format { // String formatting return ostream_format(str); } - static inline auto _fmt(const tstring_view& str) -> ostream_format + inline auto _fmt(const tstring_view& str) -> ostream_format { // String formatting return ostream_format(str); } #ifdef SEQ_HAS_CPP_17 - static inline ostream_format fmt(const std::string_view& str) + inline ostream_format fmt(const std::string_view& str) { // String formatting return ostream_format(tstring_view(str.data(), str.size())); } - static inline ostream_format _fmt(const std::string_view& str) + inline ostream_format _fmt(const std::string_view& str) { // String formatting return ostream_format(tstring_view(str.data(), str.size())); @@ -2487,227 +2487,227 @@ namespace seq #endif template - static inline auto e(T val = T()) -> ostream_format + inline auto e(T val = T()) -> ostream_format { // Helper function for floating point formatting return fmt(val,'e'); } template - static inline auto _e(T val = T()) -> ostream_format + inline auto _e(T val = T()) -> ostream_format { // Helper function for floating point formatting return _fmt(val, 'e'); } template - static inline auto E(T val = T()) -> ostream_format + inline auto E(T val = T()) -> ostream_format { // Helper function for floating point formatting return fmt(val, 'E'); } template - static inline auto _E(T val = T()) -> ostream_format + inline auto _E(T val = T()) -> ostream_format { // Helper function for floating point formatting return _fmt(val, 'E'); } template - static inline auto g(T val = T()) -> ostream_format + inline auto g(T val = T()) -> ostream_format { // Helper function for floating point formatting return fmt(val, 'g'); } template - static inline auto _g(T val = T()) -> ostream_format + inline auto _g(T val = T()) -> ostream_format { // Helper function for floating point formatting return _fmt(val, 'g'); } template - static inline auto G(T val = T()) -> ostream_format + inline auto G(T val = T()) -> ostream_format { // Helper function for floating point formatting return fmt(val, 'G'); } template - static inline auto _G(T val = T()) -> ostream_format + inline auto _G(T val = T()) -> ostream_format { // Helper function for floating point formatting return _fmt(val, 'G'); } template - static inline auto f(T val = T()) -> ostream_format + inline auto f(T val = T()) -> ostream_format { // Helper function for floating point formatting return fmt(val, 'f'); } template - static inline auto _f(T val = T()) -> ostream_format + inline auto _f(T val = T()) -> ostream_format { // Helper function for floating point formatting return _fmt(val, 'f'); } template - static inline auto F(T val = T()) -> ostream_format + inline auto F(T val = T()) -> ostream_format { // Helper function for floating point formatting return fmt(val, 'F'); } template - static inline auto _F(T val = T()) -> ostream_format + inline auto _F(T val = T()) -> ostream_format { // Helper function for floating point formatting return _fmt(val, 'F'); } template::type > - static inline auto dec(T val = T()) -> ostream_format + inline auto dec(T val = T()) -> ostream_format { // Helper function for integral formatting return fmt(val); } template::type > - static inline auto _dec(T val = T()) -> ostream_format + inline auto _dec(T val = T()) -> ostream_format { // Helper function for integral formatting return _fmt(val); } template::type> - static inline auto d(T val = T()) -> ostream_format + inline auto d(T val = T()) -> ostream_format { // Helper function for integral formatting return fmt(val); } template::type> - static inline auto _d(T val = T()) -> ostream_format + inline auto _d(T val = T()) -> ostream_format { // Helper function for integral formatting return _fmt(val); } template - static inline auto u(T val = T()) -> ostream_format + inline auto u(T val = T()) -> ostream_format { return fmt(val); } template - static inline auto _u(T val = T()) -> ostream_format + inline auto _u(T val = T()) -> ostream_format { return _fmt(val); } template - static inline auto hex(T val = T()) -> ostream_format + inline auto hex(T val = T()) -> ostream_format { // Helper function for integral formatting return fmt(val).base(16); } template - static inline auto _hex(T val = T()) -> ostream_format + inline auto _hex(T val = T()) -> ostream_format { // Helper function for integral formatting return _fmt(val).base(16); } template - static inline auto x(T val = T()) -> ostream_format + inline auto x(T val = T()) -> ostream_format { return fmt(val).base(16); } template - static inline auto _x(T val = T()) -> ostream_format + inline auto _x(T val = T()) -> ostream_format { return _fmt(val).base(16); } template - static inline auto X(T val = T()) -> ostream_format + inline auto X(T val = T()) -> ostream_format { return fmt(val).base(16).upper(); } template - static inline auto _X(T val = T()) -> ostream_format + inline auto _X(T val = T()) -> ostream_format { return _fmt(val).base(16).upper(); } template - static inline auto oct(T val = T()) -> ostream_format + inline auto oct(T val = T()) -> ostream_format { // Helper function for integral formatting return fmt(val).base(8); } template - static inline auto _oct(T val = T()) -> ostream_format + inline auto _oct(T val = T()) -> ostream_format { // Helper function for integral formatting return _fmt(val).base(8); } template - static inline auto o(T val = T()) -> ostream_format + inline auto o(T val = T()) -> ostream_format { return fmt(val).base(8); } template - static inline auto _o(T val = T()) -> ostream_format + inline auto _o(T val = T()) -> ostream_format { return _fmt(val).base(8); } template::type > - static inline auto bin(T val = T()) -> ostream_format + inline auto bin(T val = T()) -> ostream_format { // Helper function for integral formatting return fmt(val).base(2); } template::type > - static inline auto _bin(T val = T()) -> ostream_format + inline auto _bin(T val = T()) -> ostream_format { // Helper function for integral formatting return _fmt(val).base(2); } template - static inline auto ch(T val = T()) -> ostream_format + inline auto ch(T val = T()) -> ostream_format { // Format an integral value as a character return fmt(val).c(); } template - static inline auto _ch(T val = T()) -> ostream_format + inline auto _ch(T val = T()) -> ostream_format { // Format an integral value as a character return _fmt(val).c(); } - static inline auto str() -> ostream_format + inline auto str() -> ostream_format { // Null string formatting, used with seq::fmt(). // Ex.: fmt(pos<1, 3>(),"|", seq::str().c(20), "|", seq::str().c(20), "|"); return fmt(); } - static inline auto s() -> ostream_format + inline auto s() -> ostream_format { // Null string formatting, used with seq::fmt(). // Ex.: fmt(pos<1, 3>(),"|", seq::str().c(20), "|", seq::str().c(20), "|"); return fmt(); } - static inline auto _str() -> ostream_format + inline auto _str() -> ostream_format { // Null string formatting, used with seq::fmt(). // Ex.: fmt(pos<1, 3>(),"|", seq::str().c(20), "|", seq::str().c(20), "|"); return _fmt(); } - static inline auto _s() -> ostream_format + inline auto _s() -> ostream_format { // Null string formatting, used with seq::fmt(). // Ex.: fmt(pos<1, 3>(),"|", seq::str().c(20), "|", seq::str().c(20), "|"); @@ -2715,7 +2715,7 @@ namespace seq } /// @brief Repeat count times character c - static inline auto rep(char c, int count) -> ostream_format + inline auto rep(char c, int count) -> ostream_format { // Repeat count times character c return str().l(count).f(c); diff --git a/tests/test_format.cpp b/tests/test_format.cpp index d44f2f52..b72519c1 100644 --- a/tests/test_format.cpp +++ b/tests/test_format.cpp @@ -316,6 +316,7 @@ inline void test_format() int test_format(int , char*[]) { + SEQ_TEST_MODULE_RETURN(format, 1, test_format()); return 0; }