Refactor folly::to<>
authorMarcus Holland-Moritz <mhx@fb.com>
Wed, 6 Jul 2016 10:56:58 +0000 (03:56 -0700)
committerFacebook Github Bot 0 <facebook-github-bot-0-bot@fb.com>
Wed, 6 Jul 2016 11:08:21 +0000 (04:08 -0700)
commitad2f872bf354b62dbde66937da096565ef9ed663
tree6c7509eed84f3d9a4c795c2cf349f519f219f710
parentc9fd5aed37d8e116d4f3eed21b65a77e5d170037
Refactor folly::to<>

Summary:
This is the main diff of the series. Its main purpose is to make
the internals of folly::to<> propagate error codes instead of
throwing exceptions. Along with this, it makes the following
changes:

- Move most of the string-to-int implementation out of the header file

- Unify error/exception strings across conversion routines

- Introduce a ConversionError class that derives from std::range_error

- Capture an error code in ConversionError in addition to a string

- Optimize tolower() calls in Conv.cpp

- Introduce ConversionResult<>, which is used as the internal result wrapper

- Get rid of all error checking macros

There are quite a few benefits here.

== Faster conversions ==

For a large set of conversions, the performance is unchanged. I've removed
all benchmarks that were unchanged from the table below for simplicity.

A few things stand out:

- `follyAtoiMeasure` is consistently faster, sometimes by quite a large margin

- The cost of throwing exceptions is significantly reduced, as throwing them
  further down on the call stack will reduce the amount of stack unwinding

- String-to-boolean and string-to-float conversions are significantly faster
  when passing in a string representation (e.g. "off" or "infinity") thanks
  to the optimized tolower_ascii() call (column `New+Ascii` in the table)

- Conversions between int and float are significantly faster and almost back
  at the performance of before the undefined behaviour fix

- All string-to-(int|float|bool) conversions are consistently faster

The columns in the table are as follows:

  Original:  Original code before the undefined behaviour fix
  Fix UB:    Code with the undefined behaviour fix; this impacts mostly the
             float <-> int conversions, but appears to have a small effect
             on some other benchmarks
  New:       New code introduced by this diff, but without the tolower_ascii()
             optimization
  New+Ascii: New code, including the tolower_ascii() optimization

  ===========================================================================================
                                                   Original   Fix UB     New        New+Ascii
  folly/test/ConvBenchmark.cpp                     time/iter  time/iter  time/iter  time/iter
  ===========================================================================================
  handwrittenAtoiMeasure(1)                           3.95ns     3.95ns     3.95ns     3.95ns
  follyAtoiMeasure(1)                                 6.08ns     6.08ns     3.95ns     3.95ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(2)                           5.47ns     5.47ns     5.47ns     5.47ns
  follyAtoiMeasure(2)                                 5.77ns     5.77ns     3.95ns     3.95ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(3)                           6.08ns     6.08ns     6.08ns     6.08ns
  follyAtoiMeasure(3)                                 6.08ns     6.08ns     4.25ns     4.25ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(4)                           6.99ns     6.99ns     6.99ns     6.99ns
  follyAtoiMeasure(4)                                 6.99ns     6.99ns     4.56ns     4.56ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(5)                           7.90ns     8.20ns     7.90ns     7.90ns
  follyAtoiMeasure(5)                                 7.29ns     7.29ns     4.86ns     4.86ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(6)                           8.81ns     9.42ns     8.81ns     8.81ns
  follyAtoiMeasure(6)                                 7.29ns     7.29ns     4.86ns     4.86ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(7)                           9.72ns    10.63ns     9.72ns     9.72ns
  follyAtoiMeasure(7)                                 7.60ns     7.60ns     5.16ns     5.16ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(8)                          10.63ns    11.85ns    10.63ns    10.63ns
  follyAtoiMeasure(8)                                 8.51ns     8.51ns     6.08ns     6.08ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(9)                          11.54ns    13.07ns    11.54ns    11.54ns
  follyAtoiMeasure(9)                                 8.81ns     8.81ns     6.08ns     6.08ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(10)                         12.46ns    14.28ns    12.46ns    12.46ns
  follyAtoiMeasure(10)                                8.81ns     8.81ns     6.38ns     6.38ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(11)                         13.37ns    15.50ns    13.37ns    13.37ns
  follyAtoiMeasure(11)                                9.12ns     9.12ns     6.38ns     6.38ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(12)                         14.28ns    16.71ns    14.28ns    14.28ns
  follyAtoiMeasure(12)                               10.03ns    10.03ns     7.29ns     7.29ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(13)                         15.19ns    17.92ns    15.19ns    15.19ns
  follyAtoiMeasure(13)                               10.33ns    10.33ns     7.60ns     7.60ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(14)                         16.10ns    19.14ns    16.10ns    16.10ns
  follyAtoiMeasure(14)                               10.33ns    10.33ns     7.60ns     7.60ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(15)                         17.01ns    20.36ns    17.01ns    17.01ns
  follyAtoiMeasure(15)                               10.63ns    10.63ns     7.90ns     7.90ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(16)                         17.92ns    21.57ns    17.92ns    17.92ns
  follyAtoiMeasure(16)                               11.55ns    11.55ns     8.81ns     8.81ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(17)                         18.84ns    22.79ns    18.84ns    18.84ns
  follyAtoiMeasure(17)                               11.85ns    11.85ns     8.81ns     8.81ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(18)                         19.75ns    24.00ns    19.75ns    19.75ns
  follyAtoiMeasure(18)                               11.85ns    11.85ns     9.12ns     9.12ns
  -------------------------------------------------------------------------------------------
  handwrittenAtoiMeasure(19)                         20.66ns    25.22ns    20.66ns    20.66ns
  follyAtoiMeasure(19)                               12.16ns    12.16ns     9.11ns     9.11ns
  -------------------------------------------------------------------------------------------
  stringToBoolNumClassic                             12.76ns    12.76ns    11.96ns    12.15ns
  stringToBoolNumClassicError                         3.19us     3.18us     1.58us     1.58us
  stringToBoolStrClassic                             17.92ns    17.92ns    15.50ns     7.60ns
  stringToBoolStrClassicError                         3.21us     3.18us     1.57us     1.57us
  -------------------------------------------------------------------------------------------
  stringToFloatNumClassic                            32.96ns    32.81ns    32.10ns    31.12ns
  stringToFloatNumClassicError                        2.73us     2.69us     1.65us     1.66us
  stringToFloatStrClassic                            37.37ns    38.58ns    36.76ns    16.71ns
  stringToFloatStrClassicError                        2.87us     2.87us     1.60us     1.59us
  stringToDoubleNumClassic                           31.30ns    31.82ns    29.77ns    29.17ns
  stringToDoubleNumClassicError                       2.69us     2.66us     1.65us     1.66us
  stringToDoubleStrClassic                           37.67ns    37.67ns    35.84ns    16.71ns
  stringToDoubleStrClassicError                       2.87us     2.86us     1.58us     1.58us
  -------------------------------------------------------------------------------------------
  stringToCharSignedClassic                          16.71ns    18.08ns    15.49ns    14.59ns
  stringToCharSignedClassicError                      3.87us     3.82us     1.61us     1.61us
  stringToCharUnsignedClassic                        15.49ns    15.19ns    12.46ns    12.66ns
  stringToCharUnsignedClassicError                    2.73us     2.70us     1.62us     1.62us
  stringToIntSignedClassic                           21.26ns    19.44ns    17.92ns    18.40ns
  stringToIntSignedClassicError                       3.94us     3.89us     1.64us     1.64us
  stringToIntUnsignedClassic                         17.93ns    18.53ns    15.50ns    15.50ns
  stringToIntUnsignedClassicError                     2.72us     2.71us     1.62us     1.61us
  stringToLongLongSignedClassic                      34.63ns    30.58ns    27.04ns    27.04ns
  stringToLongLongSignedClassicError                  3.94us     3.90us     1.63us     1.63us
  stringToLongLongUnsignedClassic                    51.04ns    47.96ns    46.44ns    46.68ns
  stringToLongLongUnsignedClassicError                2.73us     2.71us     1.61us     1.61us
  -------------------------------------------------------------------------------------------
  ptrPairToCharSignedClassic                          5.16ns     5.16ns     3.34ns     3.65ns
  ptrPairToCharSignedClassicError                     3.56us     3.54us     1.61us     1.61us
  ptrPairToCharUnsignedClassic                        2.43ns     2.43ns     2.13ns     2.13ns
  ptrPairToCharUnsignedClassicError                   2.63us     2.63us     1.61us     1.61us
  ptrPairToIntSignedClassic                           6.99ns     6.99ns     5.16ns     5.16ns
  ptrPairToIntSignedClassicError                      4.08us     4.06us     1.61us     1.61us
  ptrPairToIntUnsignedClassic                         4.25ns     4.56ns     3.34ns     3.34ns
  ptrPairToIntUnsignedClassicError                    2.70us     2.70us     1.60us     1.60us
  ptrPairToLongLongSignedClassic                     12.16ns    12.16ns     9.72ns     9.72ns
  ptrPairToLongLongSignedClassicError                 4.06us     4.06us     1.61us     1.61us
  ptrPairToLongLongUnsignedClassic                   29.13ns    29.13ns    27.61ns    27.61ns
  ptrPairToLongLongUnsignedClassicError               2.71us     2.72us     1.63us     1.64us
  -------------------------------------------------------------------------------------------
  intToCharSignedClassic                            405.02ps   506.35ps   405.02ps   405.02ps
  intToCharSignedClassicError                         2.10us     2.09us     1.63us     1.64us
  intToCharUnsignedClassic                          303.79ps   303.78ps   303.77ps   303.77ps
  intToCharUnsignedClassicError                       2.10us     2.09us     1.63us     1.64us
  intToIntSignedClassic                             405.02ps   405.02ps   405.01ps   405.01ps
  intToIntSignedClassicError                          1.99us     1.98us     1.72us     1.72us
  intToIntUnsignedClassic                           405.03ps   405.03ps   379.71ps   379.71ps
  intToIntUnsignedClassicError                        2.09us     2.09us     1.63us     1.63us
  -------------------------------------------------------------------------------------------
  intToFloatClassic                                 545.11ps     3.34ns     1.23ns     1.23ns
  intToFloatClassicError                              1.67us     2.37us     1.73us     1.72us
  -------------------------------------------------------------------------------------------
  floatToFloatClassic                               759.47ps   759.47ps   759.45ps   759.45ps
  floatToFloatClassicError                            6.45us     6.44us     1.77us     1.77us
  -------------------------------------------------------------------------------------------
  floatToIntClassic                                 637.82ps     2.89ns     1.50ns     1.50ns
  floatToIntClassicError                              1.92us     2.61us     1.82us     1.83us
  ===========================================================================================

== Improved build times ==

I've checked this with gcc 4.9.3, and compile times for both ConvTest and
ConvBenchmark are reduced by roughly 10%:

  ====================================
                    original  new code
  ------------------------------------
  ConvTest.o         14.788s   13.361s
  ConvBenchmark.o    16.148s   14.578s
  ====================================

== Smaller binary size ==

Again, checked with gcc 4.9.3, stripped binaries are slightly smaller with
the new code:

  ====================================
                    original  new code
  ------------------------------------
  conv_test           761704    749384
  conv_benchmark      539632    510928
  ====================================

== Ability to add new non-throwing interfaces ==

This change sticks to the original API that will throw an exception in case
of an error. A subsequent diff will introduce non-throwing interfaces with
a minimum of additional code.

Reviewed By: ericniebler

Differential Revision: D3433856

fbshipit-source-id: 9bc976ebc181fe2f172ae47c78edf407e9ee7bb0
folly/Conv.cpp
folly/Conv.h
folly/experimental/test/DynamicParserTest.cpp
folly/test/ConvBenchmark.cpp
folly/test/ConvTest.cpp
folly/test/FormatTest.cpp
folly/test/StringTest.cpp