Commit f75c90a6 authored by Frank Tang's avatar Frank Tang Committed by Commit Bot

[Intl] Move NumberFormat to LocalizedNumberFormatter

Speed up Intl.PluralRules constructor x3.4

$python -u tools/run_perf.py --binary-override-path  \
   out/x64.release/d8 --filter "JSTests/Intl" \
   test/js-perf-test/JSTests5.json

Score for NewIntlPluralRules
BEFORE  550  581  576
AFTER  1856 1978 1996


Bug: v8:9300
Change-Id: I76b4290aa433b1049e3ee770d391b86e468e967d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1630134
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61924}
parent b179dd83
......@@ -2167,7 +2167,7 @@ void JSPluralRules::JSPluralRulesPrint(std::ostream& os) { // NOLINT
os << "\n - locale: " << Brief(locale());
os << "\n - type: " << TypeAsString();
os << "\n - icu plural rules: " << Brief(icu_plural_rules());
os << "\n - icu decimal format: " << Brief(icu_decimal_format());
os << "\n - icu_number_formatter: " << Brief(icu_number_formatter());
JSObjectPrintBody(os, *this);
}
......
......@@ -34,6 +34,7 @@
#include "unicode/formattedvalue.h"
#include "unicode/locid.h"
#include "unicode/normalizer2.h"
#include "unicode/numberformatter.h"
#include "unicode/numfmt.h"
#include "unicode/numsys.h"
#include "unicode/timezone.h"
......
......@@ -38,7 +38,8 @@ extern class JSPluralRules extends JSObject {
locale: String;
flags: Smi;
icu_plural_rules: Foreign; // Managed<icu::PluralRules>
icu_decimal_format: Foreign; // Managed<icu::DecimalFormat>
icu_number_formatter:
Foreign; // Managed<icu::number::LocalizedNumberFormatter>
}
extern class JSRelativeTimeFormat extends JSObject {
......
......@@ -485,6 +485,8 @@ Handle<String> SignDisplayString(Isolate* isolate,
return ReadOnlyRoots(isolate).auto_string_handle();
}
} // anonymous namespace
// Return the minimum integer digits by counting the number of '0' after
// "integer-width/+" in the skeleton.
// Ex: Return 15 for skeleton as
......@@ -492,7 +494,8 @@ Handle<String> SignDisplayString(Isolate* isolate,
// 1
// 123456789012345
// Return default value as 1 if there are no "integer-width/+".
int32_t MinimumIntegerDigitsFromSkeleton(const icu::UnicodeString& skeleton) {
int32_t JSNumberFormat::MinimumIntegerDigitsFromSkeleton(
const icu::UnicodeString& skeleton) {
// count the number of 0 after "integer-width/+"
icu::UnicodeString search("integer-width/+");
int32_t index = skeleton.indexOf(search);
......@@ -515,8 +518,8 @@ int32_t MinimumIntegerDigitsFromSkeleton(const icu::UnicodeString& skeleton) {
// 123
// 4567
// Set The minimum as 3 and maximum as 7.
bool FractionDigitsFromSkeleton(const icu::UnicodeString& skeleton,
int32_t* minimum, int32_t* maximum) {
bool JSNumberFormat::FractionDigitsFromSkeleton(
const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum) {
icu::UnicodeString search(".");
int32_t index = skeleton.indexOf(search);
if (index < 0) return false;
......@@ -542,8 +545,8 @@ bool FractionDigitsFromSkeleton(const icu::UnicodeString& skeleton,
// 12345
// 6789012
// Set The minimum as 5 and maximum as 12.
bool SignificantDigitsFromSkeleton(const icu::UnicodeString& skeleton,
int32_t* minimum, int32_t* maximum) {
bool JSNumberFormat::SignificantDigitsFromSkeleton(
const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum) {
icu::UnicodeString search("@");
int32_t index = skeleton.indexOf(search);
if (index < 0) return false;
......@@ -561,6 +564,8 @@ bool SignificantDigitsFromSkeleton(const icu::UnicodeString& skeleton,
return true;
}
namespace {
// Ex: percent .### rounding-mode-half-up
// Special case for "percent"
// Ex: "measure-unit/length-kilometer per-measure-unit/duration-hour .###
......@@ -642,9 +647,6 @@ Handle<JSObject> JSNumberFormat::ResolvedOptions(
icu::UnicodeString skeleton = icu_number_formatter->toSkeleton(status);
CHECK(U_SUCCESS(status));
std::string s_str;
s_str = skeleton.toUTF8String<std::string>(s_str);
// 4. Let options be ! ObjectCreate(%ObjectPrototype%).
Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
......
......@@ -17,14 +17,15 @@
#include "src/objects/intl-objects.h"
#include "src/objects/managed.h"
#include "src/objects/objects.h"
#include "unicode/numberformatter.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace U_ICU_NAMESPACE {
class NumberFormat;
class UnicodeString;
namespace number {
class LocalizedNumberFormatter;
} // namespace number
} // namespace U_ICU_NAMESPACE
namespace v8 {
......@@ -56,6 +57,13 @@ class JSNumberFormat : public JSObject {
V8_EXPORT_PRIVATE static const std::set<std::string>& GetAvailableLocales();
static int32_t MinimumIntegerDigitsFromSkeleton(
const icu::UnicodeString& skeleton);
static bool FractionDigitsFromSkeleton(const icu::UnicodeString& skeleton,
int32_t* minimum, int32_t* maximum);
static bool SignificantDigitsFromSkeleton(const icu::UnicodeString& skeleton,
int32_t* minimum, int32_t* maximum);
DECL_CAST(JSNumberFormat)
DECL_PRINTER(JSNumberFormat)
DECL_VERIFIER(JSNumberFormat)
......
......@@ -25,8 +25,9 @@ ACCESSORS(JSPluralRules, locale, String, kLocaleOffset)
SMI_ACCESSORS(JSPluralRules, flags, kFlagsOffset)
ACCESSORS(JSPluralRules, icu_plural_rules, Managed<icu::PluralRules>,
kIcuPluralRulesOffset)
ACCESSORS(JSPluralRules, icu_decimal_format, Managed<icu::DecimalFormat>,
kIcuDecimalFormatOffset)
ACCESSORS(JSPluralRules, icu_number_formatter,
Managed<icu::number::LocalizedNumberFormatter>,
kIcuNumberFormatterOffset)
inline void JSPluralRules::set_type(Type type) {
DCHECK_LT(type, Type::COUNT);
......
......@@ -10,11 +10,12 @@
#include "src/execution/isolate-inl.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-number-format.h"
#include "src/objects/js-plural-rules-inl.h"
#include "unicode/decimfmt.h"
#include "unicode/locid.h"
#include "unicode/numfmt.h"
#include "unicode/numberformatter.h"
#include "unicode/plurrule.h"
#include "unicode/unumberformatter.h"
namespace v8 {
namespace internal {
......@@ -23,8 +24,7 @@ namespace {
bool CreateICUPluralRules(Isolate* isolate, const icu::Locale& icu_locale,
JSPluralRules::Type type,
std::unique_ptr<icu::PluralRules>* pl,
std::unique_ptr<icu::DecimalFormat>* nf) {
std::unique_ptr<icu::PluralRules>* pl) {
// Make formatter from options. Numbering system is added
// to the locale as Unicode extension (if it was specified at all).
UErrorCode status = U_ZERO_ERROR;
......@@ -43,41 +43,10 @@ bool CreateICUPluralRules(Isolate* isolate, const icu::Locale& icu_locale,
}
CHECK_NOT_NULL(plural_rules.get());
std::unique_ptr<icu::DecimalFormat> number_format(
static_cast<icu::DecimalFormat*>(
icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status)));
if (U_FAILURE(status)) {
return false;
}
CHECK_NOT_NULL(number_format.get());
*pl = std::move(plural_rules);
*nf = std::move(number_format);
return true;
}
void InitializeICUPluralRules(
Isolate* isolate, const icu::Locale& icu_locale, JSPluralRules::Type type,
std::unique_ptr<icu::PluralRules>* plural_rules,
std::unique_ptr<icu::DecimalFormat>* number_format) {
bool success = CreateICUPluralRules(isolate, icu_locale, type, plural_rules,
number_format);
if (!success) {
// Remove extensions and try again.
icu::Locale no_extension_locale(icu_locale.getBaseName());
success = CreateICUPluralRules(isolate, no_extension_locale, type,
plural_rules, number_format);
if (!success) {
FATAL("Failed to create ICU PluralRules, are ICU data files missing?");
}
}
CHECK_NOT_NULL((*plural_rules).get());
CHECK_NOT_NULL((*number_format).get());
}
} // namespace
Handle<String> JSPluralRules::TypeAsString() const {
......@@ -156,31 +125,48 @@ MaybeHandle<JSPluralRules> JSPluralRules::Initialize(
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
plural_rules->set_locale(*locale_str);
icu::number::LocalizedNumberFormatter icu_number_formatter =
icu::number::NumberFormatter::withLocale(r.icu_locale)
.roundingMode(UNUM_ROUND_HALFUP);
std::unique_ptr<icu::PluralRules> icu_plural_rules;
std::unique_ptr<icu::DecimalFormat> icu_decimal_format;
InitializeICUPluralRules(isolate, r.icu_locale, type, &icu_plural_rules,
&icu_decimal_format);
bool success =
CreateICUPluralRules(isolate, r.icu_locale, type, &icu_plural_rules);
if (!success) {
// Remove extensions and try again.
icu::Locale no_extension_locale(r.icu_locale.getBaseName());
success = CreateICUPluralRules(isolate, no_extension_locale, type,
&icu_plural_rules);
icu_number_formatter =
icu::number::NumberFormatter::withLocale(no_extension_locale)
.roundingMode(UNUM_ROUND_HALFUP);
if (!success) {
FATAL("Failed to create ICU PluralRules, are ICU data files missing?");
}
}
CHECK_NOT_NULL(icu_plural_rules.get());
CHECK_NOT_NULL(icu_decimal_format.get());
// 9. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3).
Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options =
Intl::SetNumberFormatDigitOptions(isolate, options, 0, 3);
MAYBE_RETURN(maybe_digit_options, MaybeHandle<JSPluralRules>());
Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust();
icu_decimal_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
icu_decimal_format->setMinimumIntegerDigits(
digit_options.minimum_integer_digits);
icu_decimal_format->setMinimumFractionDigits(
digit_options.minimum_fraction_digits);
icu_decimal_format->setMaximumFractionDigits(
icu::number::Precision precision =
(digit_options.minimum_significant_digits > 0)
? icu::number::Precision::minMaxSignificantDigits(
digit_options.minimum_significant_digits,
digit_options.maximum_significant_digits)
: icu::number::Precision::minMaxFraction(
digit_options.minimum_fraction_digits,
digit_options.maximum_fraction_digits);
if (digit_options.minimum_significant_digits > 0) {
icu_decimal_format->setMinimumSignificantDigits(
digit_options.minimum_significant_digits);
icu_decimal_format->setMaximumSignificantDigits(
digit_options.maximum_significant_digits);
icu_number_formatter = icu_number_formatter.precision(precision);
if (digit_options.minimum_integer_digits > 1) {
icu_number_formatter =
icu_number_formatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(
digit_options.minimum_integer_digits));
}
Handle<Managed<icu::PluralRules>> managed_plural_rules =
......@@ -188,10 +174,12 @@ MaybeHandle<JSPluralRules> JSPluralRules::Initialize(
std::move(icu_plural_rules));
plural_rules->set_icu_plural_rules(*managed_plural_rules);
Handle<Managed<icu::DecimalFormat>> managed_decimal_format =
Managed<icu::DecimalFormat>::FromUniquePtr(isolate, 0,
std::move(icu_decimal_format));
plural_rules->set_icu_decimal_format(*managed_decimal_format);
Handle<Managed<icu::number::LocalizedNumberFormatter>>
managed_number_formatter =
Managed<icu::number::LocalizedNumberFormatter>::FromRawPtr(
isolate, 0,
new icu::number::LocalizedNumberFormatter(icu_number_formatter));
plural_rules->set_icu_number_formatter(*managed_number_formatter);
// 13. Return pluralRules.
return plural_rules;
......@@ -202,31 +190,20 @@ MaybeHandle<String> JSPluralRules::ResolvePlural(
icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules().raw();
CHECK_NOT_NULL(icu_plural_rules);
icu::DecimalFormat* icu_decimal_format =
plural_rules->icu_decimal_format().raw();
CHECK_NOT_NULL(icu_decimal_format);
// Currently, PluralRules doesn't implement all the options for rounding that
// the Intl spec provides; format and parse the number to round to the
// appropriate amount, then apply PluralRules.
//
// TODO(littledan): If a future ICU version supports an extended API to avoid
// this step, then switch to that API. Bug thread:
// http://bugs.icu-project.org/trac/ticket/12763
icu::UnicodeString rounded_string;
icu_decimal_format->format(number, rounded_string);
icu::number::LocalizedNumberFormatter* fmt =
plural_rules->icu_number_formatter().raw();
CHECK_NOT_NULL(fmt);
icu::Formattable formattable;
UErrorCode status = U_ZERO_ERROR;
icu_decimal_format->parse(rounded_string, formattable, status);
icu::number::FormattedNumber formatted_number =
fmt->formatDouble(number, status);
CHECK(U_SUCCESS(status));
double rounded = formattable.getDouble(status);
icu::UnicodeString result =
icu_plural_rules->select(formatted_number, status);
CHECK(U_SUCCESS(status));
icu::UnicodeString result = icu_plural_rules->select(rounded);
return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
return Intl::ToString(isolate, result);
}
namespace {
......@@ -261,36 +238,27 @@ Handle<JSObject> JSPluralRules::ResolvedOptions(
CreateDataPropertyForOptions(isolate, options, plural_rules->TypeAsString(),
"type");
icu::DecimalFormat* icu_decimal_format =
plural_rules->icu_decimal_format().raw();
CHECK_NOT_NULL(icu_decimal_format);
// This is a safe upcast as icu::DecimalFormat inherits from
// icu::NumberFormat.
icu::NumberFormat* icu_number_format =
static_cast<icu::NumberFormat*>(icu_decimal_format);
UErrorCode status = U_ZERO_ERROR;
icu::number::LocalizedNumberFormatter* icu_number_formatter =
plural_rules->icu_number_formatter().raw();
icu::UnicodeString skeleton = icu_number_formatter->toSkeleton(status);
CHECK(U_SUCCESS(status));
int min_int_digits = icu_number_format->getMinimumIntegerDigits();
CreateDataPropertyForOptions(isolate, options, min_int_digits,
CreateDataPropertyForOptions(
isolate, options,
JSNumberFormat::MinimumIntegerDigitsFromSkeleton(skeleton),
"minimumIntegerDigits");
int32_t min = 0, max = 0;
JSNumberFormat::FractionDigitsFromSkeleton(skeleton, &min, &max);
int min_fraction_digits = icu_number_format->getMinimumFractionDigits();
CreateDataPropertyForOptions(isolate, options, min_fraction_digits,
"minimumFractionDigits");
CreateDataPropertyForOptions(isolate, options, min, "minimumFractionDigits");
int max_fraction_digits = icu_number_format->getMaximumFractionDigits();
CreateDataPropertyForOptions(isolate, options, max_fraction_digits,
"maximumFractionDigits");
CreateDataPropertyForOptions(isolate, options, max, "maximumFractionDigits");
if (icu_decimal_format->areSignificantDigitsUsed()) {
int min_significant_digits =
icu_decimal_format->getMinimumSignificantDigits();
CreateDataPropertyForOptions(isolate, options, min_significant_digits,
if (JSNumberFormat::SignificantDigitsFromSkeleton(skeleton, &min, &max)) {
CreateDataPropertyForOptions(isolate, options, min,
"minimumSignificantDigits");
int max_significant_digits =
icu_decimal_format->getMaximumSignificantDigits();
CreateDataPropertyForOptions(isolate, options, max_significant_digits,
CreateDataPropertyForOptions(isolate, options, max,
"maximumSignificantDigits");
}
......@@ -299,7 +267,6 @@ Handle<JSObject> JSPluralRules::ResolvedOptions(
icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules().raw();
CHECK_NOT_NULL(icu_plural_rules);
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::StringEnumeration> categories(
icu_plural_rules->getKeywords(status));
CHECK(U_SUCCESS(status));
......
......@@ -22,8 +22,10 @@
#include "src/objects/object-macros.h"
namespace U_ICU_NAMESPACE {
class DecimalFormat;
class PluralRules;
namespace number {
class LocalizedNumberFormatter;
} // namespace number
} // namespace U_ICU_NAMESPACE
namespace v8 {
......@@ -76,7 +78,8 @@ class JSPluralRules : public JSObject {
DECL_ACCESSORS(locale, String)
DECL_INT_ACCESSORS(flags)
DECL_ACCESSORS(icu_plural_rules, Managed<icu::PluralRules>)
DECL_ACCESSORS(icu_decimal_format, Managed<icu::DecimalFormat>)
DECL_ACCESSORS(icu_number_formatter,
Managed<icu::number::LocalizedNumberFormatter>)
OBJECT_CONSTRUCTORS(JSPluralRules, JSObject);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment