[libc++][format] Improves parsing speed.

A format string like "{}" is quite common. In this case avoid parsing
the format-spec when it's not present. Before the parsing was always
called, therefore some refactoring is done to make sure the formatters
work properly when their parse member isn't called.

From the wording it's not entirely clear whether this optimization is
allowed

[tab:formatter]
```
  and the range [pc.begin(), pc.end()) from the last call to f.parse(pc).
```
Implies there's always a call to `f.parse` even when the format-spec
isn't present. Therefore this optimization isn't done for handle
classes; it's unclear whether that would break user defined formatters.

The improvements give a small reduciton is code size:
 719408	  12472	    488	 732368	  b2cd0	before
 718824	  12472	    488	 731784	  b2a88	after

The performance benefits when not using a format-spec are:

```
Comparing ./formatter_int.libcxx.out-baseline to ./formatter_int.libcxx.out
Benchmark                                                               Time             CPU      Time Old      Time New       CPU Old       CPU New
----------------------------------------------------------------------------------------------------------------------------------------------------
BM_Basic<uint32_t>                                                   -0.0688         -0.0687            67            62            67            62
BM_Basic<int32_t>                                                    -0.1105         -0.1107            73            65            73            65
BM_Basic<uint64_t>                                                   -0.1053         -0.1049            95            85            95            85
BM_Basic<int64_t>                                                    -0.0889         -0.0888            93            85            93            85
BM_BasicLow<__uint128_t>                                             -0.0655         -0.0655            96            90            96            90
BM_BasicLow<__int128_t>                                              -0.0693         -0.0694            97            90            97            90
BM_Basic<__uint128_t>                                                -0.0359         -0.0359           256           247           256           247
BM_Basic<__int128_t>                                                 -0.0414         -0.0414           239           229           239           229
```

For the cases where a format-spec is used the results remain similar,
some are faster some are slower, differing per run.

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D129426
This commit is contained in:
Mark de Wever 2022-07-09 16:14:40 +02:00
parent fd67992f9c
commit 6589729206
6 changed files with 8 additions and 30 deletions

View File

@ -47,6 +47,7 @@ public:
_LIBCPP_HIDE_FROM_ABI auto format(bool __value, auto& __ctx) const -> decltype(__ctx.out()) {
switch (__parser_.__type_) {
case __format_spec::__type::__default:
case __format_spec::__type::__string:
return __formatter::__format_bool(__value, __ctx, __parser_.__get_parsed_std_specifications(__ctx));

View File

@ -41,7 +41,7 @@ public:
}
_LIBCPP_HIDE_FROM_ABI auto format(_CharT __value, auto& __ctx) const -> decltype(__ctx.out()) {
if (__parser_.__type_ == __format_spec::__type::__char)
if (__parser_.__type_ == __format_spec::__type::__default || __parser_.__type_ == __format_spec::__type::__char)
return __formatter::__format_char(__value, __ctx.out(), __parser_.__get_parsed_std_specifications(__ctx));
if constexpr (sizeof(_CharT) <= sizeof(int))

View File

@ -207,10 +207,6 @@ _LIBCPP_HIDE_FROM_ABI auto __format_integer(
char* __end,
const char* __prefix,
int __base) -> decltype(__ctx.out()) {
_LIBCPP_ASSERT(
__specs.__alignment_ != __format_spec::__alignment::__default,
"the caller should adjust the default to the value required by the type");
char* __first = __formatter::__insert_sign(__begin, __negative, __specs.__std_.__sign_);
if (__specs.__std_.__alternate_form_ && __prefix)
while (*__prefix)
@ -280,6 +276,7 @@ _LIBCPP_HIDE_FROM_ABI auto __format_integer(
return __formatter::__format_integer(
__value, __ctx, __specs, __negative, __array.begin(), __array.end(), __value != 0 ? "0" : nullptr, 8);
}
case __format_spec::__type::__default:
case __format_spec::__type::__decimal: {
array<char, __formatter::__buffer_size<decltype(__value), 10>()> __array;
return __formatter::__format_integer(

View File

@ -59,14 +59,11 @@ struct _LIBCPP_TYPE_VIS __padding_size_result {
_LIBCPP_HIDE_FROM_ABI constexpr __padding_size_result
__padding_size(size_t __size, size_t __width, __format_spec::__alignment __align) {
_LIBCPP_ASSERT(__width > __size, "don't call this function when no padding is required");
_LIBCPP_ASSERT(__align != __format_spec::__alignment::__default,
"the caller should adjust the default to the value required by the type");
_LIBCPP_ASSERT(__align != __format_spec::__alignment::__zero_padding,
"the caller should have handled the zero-padding");
size_t __fill = __width - __size;
switch (__align) {
case __format_spec::__alignment::__default:
case __format_spec::__alignment::__zero_padding:
__libcpp_unreachable();
@ -81,6 +78,7 @@ __padding_size(size_t __size, size_t __width, __format_spec::__alignment __align
size_t __after = __fill - __before;
return {__before, __after};
}
case __format_spec::__alignment::__default:
case __format_spec::__alignment::__right:
return {__fill, 0};
}
@ -91,9 +89,6 @@ template <class _OutIt, class _CharT>
_LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, const char* __begin, const char* __first,
const char* __last, string&& __grouping, _CharT __sep,
__format_spec::__parsed_specifications<_CharT> __specs) {
_LIBCPP_ASSERT(__specs.__alignment_ != __format_spec::__alignment::__default,
"the caller should adjust the default to the value required by the type");
int __size = (__first - __begin) + // [sign][prefix]
(__last - __first) + // data
(__grouping.size() - 1); // number of separator characters

View File

@ -1040,18 +1040,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_char(__parser<_CharT
__format_spec::__process_display_type_bool_string(__parser);
}
template <class _CharT>
_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_integer(__parser<_CharT>& __parser) {
if (__parser.__alignment_ == __alignment::__default)
__parser.__alignment_ = __alignment::__right;
}
template <class _CharT>
_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_bool(__parser<_CharT>& __parser) {
switch (__parser.__type_) {
case __format_spec::__type::__default:
__parser.__type_ = __format_spec::__type::__string;
[[fallthrough]];
case __format_spec::__type::__string:
__format_spec::__process_display_type_bool_string(__parser);
break;
@ -1062,7 +1054,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_bool(__parser<_CharT>& __p
case __format_spec::__type::__decimal:
case __format_spec::__type::__hexadecimal_lower_case:
case __format_spec::__type::__hexadecimal_upper_case:
__process_display_type_integer(__parser);
break;
default:
@ -1074,8 +1065,6 @@ template <class _CharT>
_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_char(__parser<_CharT>& __parser) {
switch (__parser.__type_) {
case __format_spec::__type::__default:
__parser.__type_ = __format_spec::__type::__char;
[[fallthrough]];
case __format_spec::__type::__char:
__format_spec::__process_display_type_char(__parser);
break;
@ -1086,7 +1075,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_char(__parser<_CharT>& __p
case __format_spec::__type::__decimal:
case __format_spec::__type::__hexadecimal_lower_case:
case __format_spec::__type::__hexadecimal_upper_case:
__format_spec::__process_display_type_integer(__parser);
break;
default:
@ -1098,15 +1086,12 @@ template <class _CharT>
_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_integer(__parser<_CharT>& __parser) {
switch (__parser.__type_) {
case __format_spec::__type::__default:
__parser.__type_ = __format_spec::__type::__decimal;
[[fallthrough]];
case __format_spec::__type::__binary_lower_case:
case __format_spec::__type::__binary_upper_case:
case __format_spec::__type::__octal:
case __format_spec::__type::__decimal:
case __format_spec::__type::__hexadecimal_lower_case:
case __format_spec::__type::__hexadecimal_upper_case:
__format_spec::__process_display_type_integer(__parser);
break;
case __format_spec::__type::__char:
@ -1120,8 +1105,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_integer(__parser<_CharT>&
template <class _CharT>
_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_CharT>& __parser) {
__format_spec::__process_display_type_integer(__parser);
switch (__parser.__type_) {
case __format_spec::__type::__default:
// When no precision specified then it keeps default since that

View File

@ -376,6 +376,7 @@ __handle_replacement_field(const _CharT* __begin, const _CharT* __end,
__format::__parse_number_result __r =
__format::__parse_arg_id(__begin, __end, __parse_ctx);
bool __parse = *__r.__ptr == _CharT(':');
switch (*__r.__ptr) {
case _CharT(':'):
// The arg-id has a format-specifier, advance the input to the format-spec.
@ -405,6 +406,7 @@ __handle_replacement_field(const _CharT* __begin, const _CharT* __end,
__arg.format(__parse_ctx, __ctx);
else {
formatter<decltype(__arg), _CharT> __formatter;
if (__parse)
__parse_ctx.advance_to(__formatter.parse(__parse_ctx));
__ctx.advance_to(__formatter.format(__arg, __ctx));
}