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