I saw this function show up hot for customer: money. In local testing this is 12% faster in x86_64 AOT.
benchmark:
```dart
// @dart=2.12
import 'dart:math' as math;
void _skipOptionalSvgSpaces() {}
final int _max = double.maxFinite.round();
bool _isValidRangeInt(int x) =>
-_max <= x && x <= _max;
bool _isValidRange(double x) =>
-double.maxFinite <= x && x <= double.maxFinite;
bool _isValidExponent(double x) => -37 <= x && x <= 38;
class AsciiConstants {
const AsciiConstants._();
/// `\t` (horizontal tab).
static const int slashT = 9;
/// `\n` (newline).
static const int slashN = 10;
/// `\f` (form feed).
static const int slashF = 12;
/// `\r` (carriage return).
static const int slashR = 13;
/// ` ` (space).
static const int space = 32;
/// `+` (plus).
static const int plus = 43;
/// `,` (comma).
static const int comma = 44;
/// `-` (minus).
static const int minus = 45;
/// `.` (period).
static const int period = 46;
/// 0 (the number zero).
static const int number0 = 48;
/// 1 (the number one).
static const int number1 = 49;
/// 2 (the number two).
static const int number2 = 50;
/// 3 (the number three).
static const int number3 = 51;
/// 4 (the number four).
static const int number4 = 52;
/// 5 (the number five).
static const int number5 = 53;
/// 6 (the number six).
static const int number6 = 54;
/// 7 (the number seven).
static const int number7 = 55;
/// 8 (the number eight).
static const int number8 = 56;
/// 9 (the number nine).
static const int number9 = 57;
/// A
static const int upperA = 65;
/// C
static const int upperC = 67;
/// E
static const int upperE = 69;
/// H
static const int upperH = 72;
/// L
static const int upperL = 76;
/// M
static const int upperM = 77;
/// Q
static const int upperQ = 81;
/// S
static const int upperS = 83;
/// T
static const int upperT = 84;
/// V
static const int upperV = 86;
/// Z
static const int upperZ = 90;
/// a
static const int lowerA = 97;
/// c
static const int lowerC = 99;
/// e
static const int lowerE = 101;
/// h
static const int lowerH = 104;
/// l
static const int lowerL = 108;
/// m
static const int lowerM = 109;
/// q
static const int lowerQ = 113;
/// s
static const int lowerS = 115;
/// t
static const int lowerT = 116;
/// v
static const int lowerV = 118;
/// x
static const int lowerX = 120;
/// z
static const int lowerZ = 122;
/// `~` (tilde)
static const int tilde = 126;
}
class Foo {
Foo(this._string) : _length = _string.length;
int _idx = 0;
int _length = 0;
String _string;
@pragma('vm:prefer-inline')
int _readCodeUnit() {
if (_idx >= _length) {
return -1;
}
return _string.codeUnitAt(_idx++);
}
double _parseNumber() {
_skipOptionalSvgSpaces();
// Read the sign.
int sign = 1;
int c = _readCodeUnit();
if (c == AsciiConstants.plus) {
c = _readCodeUnit();
} else if (c == AsciiConstants.minus) {
sign = -1;
c = _readCodeUnit();
}
if ((c < AsciiConstants.number0 || c > AsciiConstants.number9) &&
c != AsciiConstants.period) {
throw StateError('First character of a number must be one of [0-9+-.].');
}
// Read the integer part, build left-to-right.
double integer = 0.0;
while (AsciiConstants.number0 <= c && c <= AsciiConstants.number9) {
integer = integer * 10 + (c - AsciiConstants.number0);
c = _readCodeUnit();
}
// Bail out early if this overflows.
if (!_isValidRange(integer)) {
throw StateError('Numeric overflow');
}
double decimal = 0.0;
if (c == AsciiConstants.period) {
// read the decimals
c = _readCodeUnit();
// There must be a least one digit following the .
if (c < AsciiConstants.number0 || c > AsciiConstants.number9)
throw StateError('There must be at least one digit following the .');
double frac = 1.0;
while (AsciiConstants.number0 <= c && c <= AsciiConstants.number9) {
frac *= 0.1;
decimal += (c - AsciiConstants.number0) * frac;
c = _readCodeUnit();
}
}
double number = integer + decimal;
number *= sign;
// read the exponent part
if (_idx < _length &&
(c == AsciiConstants.lowerE || c == AsciiConstants.upperE) &&
(_string.codeUnitAt(_idx) != AsciiConstants.lowerX &&
_string.codeUnitAt(_idx) != AsciiConstants.lowerM)) {
c = _readCodeUnit();
// read the sign of the exponent
bool exponentIsNegative = false;
if (c == AsciiConstants.plus) {
c = _readCodeUnit();
} else if (c == AsciiConstants.minus) {
c = _readCodeUnit();
exponentIsNegative = true;
}
// There must be an exponent
if (c < AsciiConstants.number0 || c > AsciiConstants.number9)
throw StateError('Missing exponent');
double exponent = 0.0;
while (c >= AsciiConstants.number0 && c <= AsciiConstants.number9) {
exponent *= 10.0;
exponent += c - AsciiConstants.number0;
c = _readCodeUnit();
}
if (exponentIsNegative) {
exponent = -exponent;
}
// Make sure exponent is valid.
if (!_isValidExponent(exponent)) {
throw StateError('Invalid exponent $exponent');
}
if (exponent != 0) {
number *= math.pow(10.0, exponent);
}
}
// Don't return Infinity() or NaN().
if (!_isValidRange(number)) {
throw StateError('Numeric overflow');
}
// At this stage, c contains an unprocessed character, and _idx has
// already been incremented.
// If c == -1, the input was already at the end of the string, so no
// further processing needs to occur.
if (c != -1) {
--_idx; // Put the unprocessed character back.
// if (mode & kAllowTrailingWhitespace)
_skipOptionalSvgSpacesOrDelimiter();
}
return number;
}
void _skipOptionalSvgSpacesOrDelimiter() {}
}
void main() {
int numIterations = 100000000;
{
Stopwatch stopWatch = Stopwatch();
stopWatch.start();
for (int i = 0; i < numIterations; ++i) {
Foo('1234.356')._parseNumber();
}
stopWatch.stop();
print('function ${stopWatch.elapsedMicroseconds / numIterations.toDouble()} µs');
}
}
```
I saw this function show up hot for
customer: money
. In local testing this is 12% faster in x86_64 AOT.benchmark:
```dart // @dart=2.12 import 'dart:math' as math; void _skipOptionalSvgSpaces() {} final int _max = double.maxFinite.round(); bool _isValidRangeInt(int x) => -_max <= x && x <= _max; bool _isValidRange(double x) => -double.maxFinite <= x && x <= double.maxFinite; bool _isValidExponent(double x) => -37 <= x && x <= 38; class AsciiConstants { const AsciiConstants._(); /// `\t` (horizontal tab). static const int slashT = 9; /// `\n` (newline). static const int slashN = 10; /// `\f` (form feed). static const int slashF = 12; /// `\r` (carriage return). static const int slashR = 13; /// ` ` (space). static const int space = 32; /// `+` (plus). static const int plus = 43; /// `,` (comma). static const int comma = 44; /// `-` (minus). static const int minus = 45; /// `.` (period). static const int period = 46; /// 0 (the number zero). static const int number0 = 48; /// 1 (the number one). static const int number1 = 49; /// 2 (the number two). static const int number2 = 50; /// 3 (the number three). static const int number3 = 51; /// 4 (the number four). static const int number4 = 52; /// 5 (the number five). static const int number5 = 53; /// 6 (the number six). static const int number6 = 54; /// 7 (the number seven). static const int number7 = 55; /// 8 (the number eight). static const int number8 = 56; /// 9 (the number nine). static const int number9 = 57; /// A static const int upperA = 65; /// C static const int upperC = 67; /// E static const int upperE = 69; /// H static const int upperH = 72; /// L static const int upperL = 76; /// M static const int upperM = 77; /// Q static const int upperQ = 81; /// S static const int upperS = 83; /// T static const int upperT = 84; /// V static const int upperV = 86; /// Z static const int upperZ = 90; /// a static const int lowerA = 97; /// c static const int lowerC = 99; /// e static const int lowerE = 101; /// h static const int lowerH = 104; /// l static const int lowerL = 108; /// m static const int lowerM = 109; /// q static const int lowerQ = 113; /// s static const int lowerS = 115; /// t static const int lowerT = 116; /// v static const int lowerV = 118; /// x static const int lowerX = 120; /// z static const int lowerZ = 122; /// `~` (tilde) static const int tilde = 126; } class Foo { Foo(this._string) : _length = _string.length; int _idx = 0; int _length = 0; String _string; @pragma('vm:prefer-inline') int _readCodeUnit() { if (_idx >= _length) { return -1; } return _string.codeUnitAt(_idx++); } double _parseNumber() { _skipOptionalSvgSpaces(); // Read the sign. int sign = 1; int c = _readCodeUnit(); if (c == AsciiConstants.plus) { c = _readCodeUnit(); } else if (c == AsciiConstants.minus) { sign = -1; c = _readCodeUnit(); } if ((c < AsciiConstants.number0 || c > AsciiConstants.number9) && c != AsciiConstants.period) { throw StateError('First character of a number must be one of [0-9+-.].'); } // Read the integer part, build left-to-right. double integer = 0.0; while (AsciiConstants.number0 <= c && c <= AsciiConstants.number9) { integer = integer * 10 + (c - AsciiConstants.number0); c = _readCodeUnit(); } // Bail out early if this overflows. if (!_isValidRange(integer)) { throw StateError('Numeric overflow'); } double decimal = 0.0; if (c == AsciiConstants.period) { // read the decimals c = _readCodeUnit(); // There must be a least one digit following the . if (c < AsciiConstants.number0 || c > AsciiConstants.number9) throw StateError('There must be at least one digit following the .'); double frac = 1.0; while (AsciiConstants.number0 <= c && c <= AsciiConstants.number9) { frac *= 0.1; decimal += (c - AsciiConstants.number0) * frac; c = _readCodeUnit(); } } double number = integer + decimal; number *= sign; // read the exponent part if (_idx < _length && (c == AsciiConstants.lowerE || c == AsciiConstants.upperE) && (_string.codeUnitAt(_idx) != AsciiConstants.lowerX && _string.codeUnitAt(_idx) != AsciiConstants.lowerM)) { c = _readCodeUnit(); // read the sign of the exponent bool exponentIsNegative = false; if (c == AsciiConstants.plus) { c = _readCodeUnit(); } else if (c == AsciiConstants.minus) { c = _readCodeUnit(); exponentIsNegative = true; } // There must be an exponent if (c < AsciiConstants.number0 || c > AsciiConstants.number9) throw StateError('Missing exponent'); double exponent = 0.0; while (c >= AsciiConstants.number0 && c <= AsciiConstants.number9) { exponent *= 10.0; exponent += c - AsciiConstants.number0; c = _readCodeUnit(); } if (exponentIsNegative) { exponent = -exponent; } // Make sure exponent is valid. if (!_isValidExponent(exponent)) { throw StateError('Invalid exponent $exponent'); } if (exponent != 0) { number *= math.pow(10.0, exponent); } } // Don't return Infinity() or NaN(). if (!_isValidRange(number)) { throw StateError('Numeric overflow'); } // At this stage, c contains an unprocessed character, and _idx has // already been incremented. // If c == -1, the input was already at the end of the string, so no // further processing needs to occur. if (c != -1) { --_idx; // Put the unprocessed character back. // if (mode & kAllowTrailingWhitespace) _skipOptionalSvgSpacesOrDelimiter(); } return number; } void _skipOptionalSvgSpacesOrDelimiter() {} } void main() { int numIterations = 100000000; { Stopwatch stopWatch = Stopwatch(); stopWatch.start(); for (int i = 0; i < numIterations; ++i) { Foo('1234.356')._parseNumber(); } stopWatch.stop(); print('function ${stopWatch.elapsedMicroseconds / numIterations.toDouble()} µs'); } } ```