upd: [imp] optimize the performance of fmt a little bit

This commit is contained in:
mutouyun 2022-12-03 16:16:55 +08:00
parent b98329c062
commit 73443ee0a6
4 changed files with 135 additions and 65 deletions

View File

@ -0,0 +1,69 @@
#include <tuple>
#include "benchmark/benchmark.h"
#include "fmt/format.h"
#include "fmt/chrono.h"
#include "libimp/fmt.h"
namespace {
void imp_fmt_string(benchmark::State &state) {
for (auto _ : state) {
imp::fmt("hello world. hello world. hello world. hello world. hello world.");
}
}
void fmt_format_string(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("hello world. hello world. hello world. hello world. hello world.");
}
}
void imp_fmt_int(benchmark::State &state) {
for (auto _ : state) {
imp::fmt(654321);
}
}
void fmt_format_int(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("{}", 654321);
}
}
void imp_fmt_float(benchmark::State &state) {
for (auto _ : state) {
imp::fmt(654.321);
}
}
void fmt_format_float(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("{}", 654.321);
}
}
void imp_fmt_chrono(benchmark::State &state) {
for (auto _ : state) {
imp::fmt(std::chrono::system_clock::now());
}
}
void fmt_format_chrono(benchmark::State &state) {
for (auto _ : state) {
std::ignore = fmt::format("{}", std::chrono::system_clock::now());
}
}
} // namespace
BENCHMARK(imp_fmt_string);
BENCHMARK(fmt_format_string);
BENCHMARK(imp_fmt_int);
BENCHMARK(fmt_format_int);
BENCHMARK(imp_fmt_float);
BENCHMARK(fmt_format_float);
BENCHMARK(imp_fmt_chrono);
BENCHMARK(fmt_format_chrono);

View File

@ -5,14 +5,14 @@
namespace { namespace {
void BM_imp_log_no_output(benchmark::State& state) { void imp_log_no_output(benchmark::State &state) {
imp::log::grip log {__func__, {}}; imp::log::grip log {__func__, {}};
for (auto _ : state) { for (auto _ : state) {
log.debug("hello log."); log.debug("hello log.");
} }
} }
void BM_imp_log_gripper(benchmark::State& state) { void imp_log_gripper(benchmark::State &state) {
imp::log::grip log {__func__, {}}; imp::log::grip log {__func__, {}};
for (auto _ : state) { for (auto _ : state) {
log.info("hello log."); log.info("hello log.");
@ -21,5 +21,5 @@ void BM_imp_log_gripper(benchmark::State& state) {
} // namespace } // namespace
BENCHMARK(BM_imp_log_no_output); BENCHMARK(imp_log_no_output);
BENCHMARK(BM_imp_log_gripper); BENCHMARK(imp_log_gripper);

View File

@ -3,6 +3,9 @@
* @author mutouyun (orz@orzz.org) * @author mutouyun (orz@orzz.org)
* @brief String formatting. * @brief String formatting.
* @date 2022-11-26 * @date 2022-11-26
*
* @remarks The current performance is not high,
* because I use std::sprintf directly for formatting for convenience.
*/ */
#pragma once #pragma once
@ -11,7 +14,7 @@
#include <type_traits> #include <type_traits>
#include <chrono> // std::chrono::time_point #include <chrono> // std::chrono::time_point
#include <cstddef> #include <cstddef>
#include <ctime> // std::tm #include <ctime> // std::tm, std::localtime
#include "libimp/def.h" #include "libimp/def.h"
#include "libimp/fmt_cpo.h" #include "libimp/fmt_cpo.h"
@ -21,12 +24,23 @@
LIBIMP_NAMESPACE_BEG_ LIBIMP_NAMESPACE_BEG_
/**
* @brief The format string reference wrapper.
*/
template <typename T> template <typename T>
struct fmt_ref { struct fmt_ref {
span<char const> fstr; span<char const> fstr;
T param; T param;
}; };
/**
* @brief Conversion specifiers.
*
* @remarks Just like printf, the format string is of the form
* [flags][field_width][.precision][conversion_character]
*
* @see http://personal.ee.surrey.ac.uk/Personal/R.Bowden/C/printf.html
*/
template <std::size_t N> template <std::size_t N>
auto spec(char const (&fstr)[N]) noexcept { auto spec(char const (&fstr)[N]) noexcept {
return [&fstr](auto &&arg) noexcept { return [&fstr](auto &&arg) noexcept {
@ -35,9 +49,16 @@ auto spec(char const (&fstr)[N]) noexcept {
}; };
} }
template <typename... A> /// @brief String formatting function.
std::string fmt(A &&...args) {
std::string joined; template <typename A>
std::string fmt(A &&a) {
return fmt_to_string(std::forward<A>(a));
}
template <typename A1, typename... A>
std::string fmt(A1 &&a1, A &&...args) {
std::string joined(fmt(std::forward<A1>(a1)));
LIBIMP_UNUSED auto unfold = { LIBIMP_UNUSED auto unfold = {
joined.append(fmt_to_string(std::forward<A>(args)))... joined.append(fmt_to_string(std::forward<A>(args)))...
}; };
@ -45,24 +66,26 @@ std::string fmt(A &&...args) {
} }
/// @brief Return the string directly. /// @brief Return the string directly.
LIBIMP_EXPORT std::string to_string(std::string const &a) noexcept; inline char const *to_string(char const *a) noexcept { return (a == nullptr) ? "" : a; }
LIBIMP_EXPORT std::string to_string(std::string &&a) noexcept; template <std::size_t N>
LIBIMP_EXPORT std::string to_string(std::string const &a, span<char const> fstr) noexcept; inline char const *to_string(char const (&a)[N]) noexcept { return a; }
inline std::string to_string(std::string const &a) noexcept { return a; }
inline std::string to_string(std::string &&a) noexcept { return std::move(a); }
LIBIMP_EXPORT std::string to_string(char const *a, span<char const> fstr) noexcept;
inline std::string to_string(std::string const &a, span<char const> fstr) noexcept { return to_string(a.c_str(), fstr); }
/// @brief Character to string conversion. /// @brief Character to string conversion.
/// @return an empty string if the conversion fails /// @return an empty string if the conversion fails
LIBIMP_EXPORT std::string to_string(char a) noexcept; inline std::string to_string(char a) noexcept { return {a}; }
LIBIMP_EXPORT std::string to_string(wchar_t a) noexcept; LIBIMP_EXPORT std::string to_string(wchar_t a) noexcept;
LIBIMP_EXPORT std::string to_string(char16_t a) noexcept; LIBIMP_EXPORT std::string to_string(char16_t a) noexcept;
LIBIMP_EXPORT std::string to_string(char32_t a) noexcept; LIBIMP_EXPORT std::string to_string(char32_t a) noexcept;
#if defined(LIBIMP_CPP_20) #if defined(LIBIMP_CPP_20)
LIBIMP_EXPORT std::string to_string(char8_t a) noexcept; LIBIMP_EXPORT std::string to_string(char8_t a) noexcept { return to_string((char)a); }
#endif // defined(LIBIMP_CPP_20) #endif // defined(LIBIMP_CPP_20)
/// @brief Conversion of numeric types to strings. /// @brief Conversion of numeric types to strings.
/// @return an empty string if the conversion fails /// @return an empty string if the conversion fails
LIBIMP_EXPORT std::string to_string(signed char a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(unsigned char a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(signed short a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(signed short a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(unsigned short a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(unsigned short a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(signed int a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(signed int a, span<char const> fstr = {}) noexcept;
@ -71,26 +94,47 @@ LIBIMP_EXPORT std::string to_string(signed long a, span<char const> fstr = {}) n
LIBIMP_EXPORT std::string to_string(unsigned long a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(unsigned long a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(signed long long a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(signed long long a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(unsigned long long a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(unsigned long long a, span<char const> fstr = {}) noexcept;
inline std::string to_string( signed char a, span<char const> fstr = {}) noexcept { return to_string( (int)a, fstr); }
inline std::string to_string(unsigned char a, span<char const> fstr = {}) noexcept { return to_string((unsigned)a, fstr); }
/// @brief Conversion of floating point type to strings. /// @brief Conversion of floating point type to strings.
/// @return an empty string if the conversion fails /// @return an empty string if the conversion fails
LIBIMP_EXPORT std::string to_string(float a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(double a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(double a, span<char const> fstr = {}) noexcept;
LIBIMP_EXPORT std::string to_string(long double a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(long double a, span<char const> fstr = {}) noexcept;
inline std::string to_string(float a, span<char const> fstr = {}) noexcept { return to_string((double)a, fstr); }
/// @brief Pointer. /// @brief Pointer.
LIBIMP_EXPORT std::string to_string(std::nullptr_t) noexcept; inline std::string to_string(std::nullptr_t) noexcept { return "null"; }
template <typename T, template <typename T,
typename = std::enable_if_t<std::is_same<T, void>::value>> typename = std::enable_if_t<std::is_same<T, void>::value>>
LIBIMP_EXPORT std::string to_string(T const volatile *a) noexcept; LIBIMP_EXPORT std::string to_string(T const volatile *a) noexcept;
/// @brief Date and time. /// @brief Date and time.
LIBIMP_EXPORT std::string to_string(std::tm const &a, span<char const> fstr = {}) noexcept; LIBIMP_EXPORT std::string to_string(std::tm const &a, span<char const> fstr = {}) noexcept;
namespace detail { namespace detail {
LIBIMP_EXPORT std::string time_to_string(std::time_t tt, span<char const> fstr) noexcept;
/**
* @brief Convert std::time_t to std::string.
* @return an empty string if the conversion fails
*/
inline std::string time_to_string(std::time_t tt, span<char const> fstr) noexcept {
#if defined(LIBIMP_CC_MSVC)
/// @see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-s-localtime32-s-localtime64-s
std::tm tm {};
if (::localtime_s(&tm, &tt) != 0) {
return {};
}
return to_string(tm, fstr);
#else
return to_string(*std::localtime(&tt), fstr);
#endif
}
} // namespace detail } // namespace detail
template <class Clock, class Duration> template <class Clock, class Duration>
LIBIMP_EXPORT std::string to_string(std::chrono::time_point<Clock, Duration> const &a, span<char const> fstr = {}) noexcept { std::string to_string(std::chrono::time_point<Clock, Duration> const &a, span<char const> fstr = {}) noexcept {
return detail::time_to_string(std::chrono::system_clock::to_time_t(a), fstr); return detail::time_to_string(std::chrono::system_clock::to_time_t(a), fstr);
} }

View File

@ -1,4 +1,3 @@
#define _CRT_SECURE_NO_WARNINGS
#include "libimp/fmt.h" #include "libimp/fmt.h"
#include <cstdio> // std::snprintf #include <cstdio> // std::snprintf
@ -98,21 +97,9 @@ std::string sprintf(F fop, span<char const> fstr, span<char const> s, A a) noexc
} // namespace } // namespace
std::string to_string(std::string const &a) noexcept { std::string to_string(char const *a, span<char const> fstr) noexcept {
return a; if (a == nullptr) return {};
} return ::LIBIMP::sprintf(fmt_of, fstr, "s", a);
std::string to_string(std::string &&a) noexcept {
return std::move(a);
}
std::string to_string(std::string const &a, span<char const> fstr) noexcept {
if (a.empty()) return {};
return ::LIBIMP::sprintf(fmt_of, fstr, "s", a.c_str());
}
std::string to_string(char a) noexcept {
return {a};
} }
std::string to_string(wchar_t a) noexcept { std::string to_string(wchar_t a) noexcept {
@ -145,20 +132,6 @@ std::string to_string(char32_t a) noexcept {
} }
} }
#if defined(LIBIMP_CPP_20)
std::string to_string(char8_t a) noexcept {
return to_string((char)a);
}
#endif // defined(LIBIMP_CPP_20)
std::string to_string(signed char a, span<char const> fstr) noexcept {
return to_string((int)a, fstr);
}
std::string to_string(unsigned char a, span<char const> fstr) noexcept {
return to_string((unsigned)a, fstr);
}
std::string to_string(signed short a, span<char const> fstr) noexcept { std::string to_string(signed short a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_signed, fstr, "h", a); return ::LIBIMP::sprintf(fmt_of_signed, fstr, "h", a);
} }
@ -191,10 +164,6 @@ std::string to_string(unsigned long long a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "ll", a); return ::LIBIMP::sprintf(fmt_of_unsigned, fstr, "ll", a);
} }
std::string to_string(float a, span<char const> fstr) noexcept {
return to_string((double)a, fstr);
}
std::string to_string(double a, span<char const> fstr) noexcept { std::string to_string(double a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_float, fstr, "", a); return ::LIBIMP::sprintf(fmt_of_float, fstr, "", a);
} }
@ -203,10 +172,6 @@ std::string to_string(long double a, span<char const> fstr) noexcept {
return ::LIBIMP::sprintf(fmt_of_float, fstr, "L", a); return ::LIBIMP::sprintf(fmt_of_float, fstr, "L", a);
} }
std::string to_string(std::nullptr_t) noexcept {
return "null";
}
template <> template <>
std::string to_string<void, void>(void const volatile *a) noexcept { std::string to_string<void, void>(void const volatile *a) noexcept {
if (a == nullptr) { if (a == nullptr) {
@ -228,12 +193,4 @@ std::string to_string(std::tm const &a, span<char const> fstr) noexcept {
} }
} }
namespace detail {
std::string time_to_string(std::time_t tt, span<char const> fstr) noexcept {
return to_string(*std::localtime(&tt), fstr);
}
} // namespace detail
LIBIMP_NAMESPACE_END_ LIBIMP_NAMESPACE_END_