Skip to content

Commit 49bced4

Browse files
authored
[libc++] Add basic constant folding for std::format (llvm#107197)
``` ------------------------------------------------------------------- Benchmark old new ------------------------------------------------------------------- BM_format_string<char>/1 42.1 ns 7.67 ns BM_format_string<char>/2 22.3 ns 3.84 ns BM_format_string<char>/4 10.6 ns 1.92 ns BM_format_string<char>/8 5.31 ns 0.815 ns BM_format_string<char>/16 2.79 ns 0.480 ns BM_format_string<char>/32 1.63 ns 0.550 ns BM_format_string<char>/64 0.782 ns 0.276 ns BM_format_string<char>/128 0.397 ns 0.145 ns BM_format_string<char>/256 0.211 ns 0.066 ns BM_format_string<char>/512 0.154 ns 0.035 ns BM_format_string<char>/1024 0.146 ns 0.021 ns BM_format_string<char>/2048 0.125 ns 0.033 ns BM_format_string<char>/4096 0.097 ns 0.016 ns BM_format_string<char>/8192 0.077 ns 0.012 ns BM_format_string<char>/16384 0.066 ns 0.010 ns BM_format_string<char>/32768 0.062 ns 0.016 ns BM_format_string<char>/65536 0.062 ns 0.016 ns BM_format_string<char>/131072 0.443 ns 0.015 ns BM_format_string<char>/262144 0.629 ns 0.017 ns BM_format_string<char>/524288 0.715 ns 0.020 ns BM_format_string<char>/1048576 0.757 ns 0.020 ns BM_format_string<wchar_t>/1 38.8 ns 34.0 ns BM_format_string<wchar_t>/2 19.4 ns 16.9 ns BM_format_string<wchar_t>/4 9.88 ns 8.45 ns BM_format_string<wchar_t>/8 6.30 ns 6.47 ns BM_format_string<wchar_t>/16 3.11 ns 3.21 ns BM_format_string<wchar_t>/32 1.60 ns 1.63 ns BM_format_string<wchar_t>/64 0.899 ns 0.925 ns BM_format_string<wchar_t>/128 0.676 ns 0.693 ns BM_format_string<wchar_t>/256 0.658 ns 0.685 ns BM_format_string<wchar_t>/512 0.556 ns 0.531 ns BM_format_string<wchar_t>/1024 0.428 ns 0.402 ns BM_format_string<wchar_t>/2048 0.328 ns 0.319 ns BM_format_string<wchar_t>/4096 0.276 ns 0.274 ns BM_format_string<wchar_t>/8192 0.252 ns 0.251 ns BM_format_string<wchar_t>/16384 0.248 ns 0.246 ns BM_format_string<wchar_t>/32768 0.229 ns 0.232 ns BM_format_string<wchar_t>/65536 0.248 ns 0.246 ns BM_format_string<wchar_t>/131072 0.250 ns 0.240 ns BM_format_string<wchar_t>/262144 3.03 ns 3.03 ns BM_format_string<wchar_t>/524288 3.14 ns 3.15 ns BM_format_string<wchar_t>/1048576 3.60 ns 3.61 ns BM_string_without_formatting<char> 32.2 ns 0.470 ns BM_string_without_formatting<wchar_t> 38.8 ns 10.2 ns ```
1 parent e7e4d99 commit 49bced4

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

libcxx/include/__format/format_functions.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#define _LIBCPP___FORMAT_FORMAT_FUNCTIONS
1212

1313
#include <__algorithm/clamp.h>
14+
#include <__algorithm/ranges_find_first_of.h>
15+
#include <__chrono/statically_widen.h>
1416
#include <__concepts/convertible_to.h>
1517
#include <__concepts/same_as.h>
1618
#include <__config>
@@ -36,6 +38,7 @@
3638
#include <__iterator/iterator_traits.h> // iter_value_t
3739
#include <__variant/monostate.h>
3840
#include <array>
41+
#include <optional>
3942
#include <string>
4043
#include <string_view>
4144

@@ -447,10 +450,47 @@ format_to(_OutIt __out_it, wformat_string<_Args...> __fmt, _Args&&... __args) {
447450
}
448451
# endif
449452

453+
// Try constant folding the format string instead of going through the whole formatting machinery. If there is no
454+
// constant folding no extra code should be emitted (with optimizations enabled) and the function returns nullopt. When
455+
// constant folding is successful, the formatting is performed and the resulting string is returned.
456+
namespace __format {
457+
template <class _CharT>
458+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<basic_string<_CharT>> __try_constant_folding(
459+
basic_string_view<_CharT> __fmt,
460+
basic_format_args<basic_format_context<back_insert_iterator<__format::__output_buffer<_CharT>>, _CharT>> __args) {
461+
// Fold strings not containing '{' or '}' to just return the string
462+
if (bool __is_identity = [&] [[__gnu__::__pure__]] // Make sure the compiler knows this call can be eliminated
463+
{ return std::ranges::find_first_of(__fmt, array{'{', '}'}) == __fmt.end(); }();
464+
__builtin_constant_p(__is_identity) && __is_identity)
465+
return basic_string<_CharT>{__fmt};
466+
467+
// Fold '{}' to the appropriate conversion function
468+
if (auto __only_first_arg = __fmt == _LIBCPP_STATICALLY_WIDEN(_CharT, "{}");
469+
__builtin_constant_p(__only_first_arg) && __only_first_arg) {
470+
if (auto __arg = __args.get(0); __builtin_constant_p(__arg.__type_)) {
471+
return std::__visit_format_arg(
472+
[]<class _Tp>(_Tp&& __argument) -> optional<basic_string<_CharT>> {
473+
if constexpr (is_same_v<remove_cvref_t<_Tp>, basic_string_view<_CharT>>) {
474+
return basic_string<_CharT>{__argument};
475+
} else {
476+
return nullopt;
477+
}
478+
},
479+
__arg);
480+
}
481+
}
482+
483+
return nullopt;
484+
}
485+
} // namespace __format
486+
450487
// TODO FMT This needs to be a template or std::to_chars(floating-point) availability markup
451488
// fires too eagerly, see http://llvm.org/PR61563.
452489
template <class = void>
453490
[[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI string vformat(string_view __fmt, format_args __args) {
491+
auto __result = __format::__try_constant_folding(__fmt, __args);
492+
if (__result.has_value())
493+
return *std::move(__result);
454494
__format::__allocating_buffer<char> __buffer;
455495
std::vformat_to(__buffer.__make_output_iterator(), __fmt, __args);
456496
return string{__buffer.__view()};
@@ -462,6 +502,9 @@ template <class = void>
462502
template <class = void>
463503
[[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI wstring
464504
vformat(wstring_view __fmt, wformat_args __args) {
505+
auto __result = __format::__try_constant_folding(__fmt, __args);
506+
if (__result.has_value())
507+
return *std::move(__result);
465508
__format::__allocating_buffer<wchar_t> __buffer;
466509
std::vformat_to(__buffer.__make_output_iterator(), __fmt, __args);
467510
return wstring{__buffer.__view()};

libcxx/test/benchmarks/format/format.bench.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,15 @@ BENCHMARK(BM_format_string<char>)->RangeMultiplier(2)->Range(1, 1 << 20);
3535
BENCHMARK(BM_format_string<wchar_t>)->RangeMultiplier(2)->Range(1, 1 << 20);
3636
#endif
3737

38+
template <class CharT>
39+
static void BM_string_without_formatting(benchmark::State& state) {
40+
for (auto _ : state) {
41+
benchmark::DoNotOptimize(std::format(CSTR("Hello, World!")));
42+
}
43+
}
44+
BENCHMARK(BM_string_without_formatting<char>);
45+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
46+
BENCHMARK(BM_string_without_formatting<wchar_t>);
47+
#endif
48+
3849
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)