[libcxx-commits] [PATCH] D59178: Speedup to_string and to_wstring for integers using stack buffer and SSO

Afanasyev Ivan via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Sat Mar 9 11:27:58 PST 2019


ivafanas added a comment.

Code correctness was checked via

  make llvm-check

Please, let me know if this command doesn't do what is expected according to documentation (I'm newby in clang project).

Performance tests:
http://quick-bench.com/wOBLE4yDNQp0x7RGg7ex32atgQY

In the case of quick-bench cache expired, the testing code is:

  #include <string>
  #include <type_traits>
  
  namespace ref
  {
  
  template<typename S, typename P, typename V >
  inline
  S
  as_string(P sprintf_like, S s, const typename S::value_type* fmt, V a)
  {
      typedef typename S::size_type size_type;
      size_type available = s.size();
      while (true)
      {
          int status = sprintf_like(&s[0], available + 1, fmt, a);
          if ( status >= 0 )
          {
              size_type used = static_cast<size_type>(status);
              if ( used <= available )
              {
                  s.resize( used );
                  break;
              }
              available = used; // Assume this is advice of how much space we need.
          }
          else
              available = available * 2 + 1;
          s.resize(available);
      }
      return s;
  }
  
  template <class S, class V, bool = std::is_floating_point<V>::value>
  struct initial_string;
  
  template <class V, bool b>
  struct initial_string<std::string, V, b>
  {
      std::string
      operator()() const
      {
          std::string s;
          s.resize(s.capacity());
          return s;
      }
  };
  
  template <class V>
  struct initial_string<std::wstring, V, false>
  {
      std::wstring
      operator()() const
      {
          const size_t n = (std::numeric_limits<unsigned long long>::digits / 3)
            + ((std::numeric_limits<unsigned long long>::digits % 3) != 0)
            + 1;
          std::wstring s(n, wchar_t());
          s.resize(s.capacity());
          return s;
      }
  };
  
  template <class V>
  struct initial_string<std::wstring, V, true>
  {
      std::wstring
      operator()() const
      {
          std::wstring s(20, wchar_t());
          s.resize(s.capacity());
          return s;
      }
  };
  
  typedef int (*wide_printf)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...);
  
  inline
  wide_printf
  get_swprintf()
  {
  #ifndef _LIBCPP_MSVCRT
      return swprintf;
  #else
      return static_cast<int (__cdecl*)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...)>(_snwprintf);
  #endif
  }
  
  std::string __attribute__ ((noinline)) to_string(int val)
  {
      return as_string(snprintf, initial_string<std::string, int>()(), "%d", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(unsigned val)
  {
      return as_string(snprintf, initial_string<std::string, unsigned>()(), "%u", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(long val)
  {
      return as_string(snprintf, initial_string<std::string, long>()(), "%ld", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(unsigned long val)
  {
      return as_string(snprintf, initial_string<std::string, unsigned long>()(), "%lu", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(long long val)
  {
      return as_string(snprintf, initial_string<std::string, long long>()(), "%lld", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(unsigned long long val)
  {
      return as_string(snprintf, initial_string<std::string, unsigned long long>()(), "%llu", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(int val)
  {
      return as_string(get_swprintf(), initial_string<std::wstring, int>()(), L"%d", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(unsigned val)
  {
      return as_string(get_swprintf(), initial_string<std::wstring, unsigned>()(), L"%u", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(long val)
  {
      return as_string(get_swprintf(), initial_string<std::wstring, long>()(), L"%ld", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(unsigned long val)
  {
      return as_string(get_swprintf(), initial_string<std::wstring, unsigned long>()(), L"%lu", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(long long val)
  {
      return as_string(get_swprintf(), initial_string<std::wstring, long long>()(), L"%lld", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(unsigned long long val)
  {
      return as_string(get_swprintf(), initial_string<std::wstring, unsigned long long>()(), L"%llu", val);
  }
  
  }  // ref
  
  
  
  ///
  ///
  ///
  
  
  
  namespace tgt
  {
  
    template<typename S, typename P, typename V>
    inline S as_string(P sprintf_like, const typename S::value_type* fmt, V a)
    {
      constexpr size_t size = 4 * sizeof(V) + 1; // +1 for null char added by swprintf
      typename S::value_type tmp[size] = {};
      const int len = sprintf_like(tmp, size, fmt, a);
      return S(tmp, tmp + len);
    }
  
  typedef int (*wide_printf)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...);
  
  inline
  wide_printf
  get_swprintf()
  {
  #ifndef _LIBCPP_MSVCRT
      return swprintf;
  #else
      return static_cast<int (__cdecl*)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...)>(_snwprintf);
  #endif
  }
  
  std::string __attribute__ ((noinline)) to_string(int val)
  {
      return as_string<std::string>(snprintf, "%d", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(unsigned val)
  {
      return as_string<std::string>(snprintf, "%u", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(long val)
  {
      return as_string<std::string>(snprintf, "%ld", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(unsigned long val)
  {
      return as_string<std::string>(snprintf, "%lu", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(long long val)
  {
      return as_string<std::string>(snprintf, "%lld", val);
  }
  
  std::string __attribute__ ((noinline)) to_string(unsigned long long val)
  {
      return as_string<std::string>(snprintf, "%llu", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(int val)
  {
      return as_string<std::wstring>(get_swprintf(), L"%d", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(unsigned val)
  {
      return as_string<std::wstring>(get_swprintf(), L"%u", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(long val)
  {
      return as_string<std::wstring>(get_swprintf(), L"%ld", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(unsigned long val)
  {
      return as_string<std::wstring>(get_swprintf(), L"%lu", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(long long val)
  {
      return as_string<std::wstring>(get_swprintf(), L"%lld", val);
  }
  
  std::wstring __attribute__ ((noinline)) to_wstring(unsigned long long val)
  {
      return as_string<std::wstring>(get_swprintf(), L"%llu", val);
  }
  
  }  // tgt
  
  
  
  const unsigned long long one = 1;
  const unsigned long long ullmax = std::numeric_limits<unsigned long long>::max();
  
  static void to_string_ref_1(benchmark::State& state) {
    for (auto _ : state) {
      const auto x = ref::to_string(one);
      benchmark::DoNotOptimize(x);
    }
  }
  BENCHMARK(to_string_ref_1);
  
  static void to_string_tgt_1(benchmark::State& state) {
    for (auto _ : state) {
      const auto x = tgt::to_string(one);
      benchmark::DoNotOptimize(x);
    }
  }
  BENCHMARK(to_string_tgt_1);
  
  static void to_string_ref_max(benchmark::State& state) {
    for (auto _ : state) {
      const auto x = ref::to_string(ullmax);
      benchmark::DoNotOptimize(x);
    }
  }
  BENCHMARK(to_string_ref_max);
  
  static void to_string_tgt_max(benchmark::State& state) {
    for (auto _ : state) {
      const auto x = tgt::to_string(ullmax);
      benchmark::DoNotOptimize(x);
    }
  }
  BENCHMARK(to_string_tgt_max);
  
  static void to_wstring_ref_1(benchmark::State& state) {
    for (auto _ : state) {
      const auto x = ref::to_wstring(one);
      benchmark::DoNotOptimize(x);
    }
  }
  BENCHMARK(to_wstring_ref_1);
  
  static void to_wstring_tgt_1(benchmark::State& state) {
    for (auto _ : state) {
      const auto x = tgt::to_wstring(one);
      benchmark::DoNotOptimize(x);
    }
  }
  BENCHMARK(to_wstring_tgt_1);
  
  static void to_wstring_ref_max(benchmark::State& state) {
    for (auto _ : state) {
      const auto x = ref::to_wstring(ullmax);
      benchmark::DoNotOptimize(x);
    }
  }
  BENCHMARK(to_wstring_ref_max);
  
  static void to_wstring_tgt_max(benchmark::State& state) {
    for (auto _ : state) {
      const auto x = tgt::to_wstring(ullmax);
      benchmark::DoNotOptimize(x);
    }
  }
  BENCHMARK(to_wstring_tgt_max);


Repository:
  rCXX libc++

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59178/new/

https://reviews.llvm.org/D59178





More information about the libcxx-commits mailing list