Commit 4b139f76 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[intl] Cache the last used formatter if possible

Instead of caching only the default formatter, cache the last used
formatter if possible.

This is better because it's a common use case to create a formatter
in a different language and reuse it a lot, rather than create
several formatters in various languages.

Running the following benchmark:
```
let i = 0;

function toLocaleString() {
  i++;
  return i.toLocaleString();
}

i = 0;
function toLocaleStringWithLocale() {
  i++;
  return i.toLocaleString('en-US');
}

const functions = [toLocaleString, toLocaleStringWithLocale];

for (const f of functions) {
  let start = performance.now();
  for (let i = 0; i < 10e5; i++) {
    f();
  }
  let end = performance.now();
  print(`${f.name}: ${end - start}`);
}
```
sees the following improvements:

With this patch:
  toLocaleString: 384.292
  toLocaleStringWithLocale: 450.48900000000003

Without this patch:
  toLocaleString: 341.952
  toLocaleStringWithLocale: 23439.694

This a little over 50x improvement.

Bug: chromium:926075
Change-Id: I0e316e959c90243e175df985854832a7abddbf54
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2536461
Commit-Queue: Sathya Gunasekaran  <gsathya@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71188}
parent a18a674b
......@@ -4561,13 +4561,41 @@ void Isolate::CollectSourcePositionsForAllBytecodeArrays() {
}
#ifdef V8_INTL_SUPPORT
icu::UMemory* Isolate::get_cached_icu_object(ICUObjectCacheType cache_type) {
return icu_object_cache_[cache_type].get();
namespace {
std::string GetStringFromLocale(Handle<Object> locales_obj) {
DCHECK(locales_obj->IsString() || locales_obj->IsUndefined());
if (locales_obj->IsString()) {
return std::string(String::cast(*locales_obj).ToCString().get());
}
return "";
}
} // namespace
icu::UMemory* Isolate::get_cached_icu_object(ICUObjectCacheType cache_type,
Handle<Object> locales_obj) {
std::string locale = GetStringFromLocale(locales_obj);
auto value = icu_object_cache_.find(cache_type);
if (value == icu_object_cache_.end()) return nullptr;
ICUCachePair pair = value->second;
if (pair.first != locale) return nullptr;
return pair.second.get();
}
void Isolate::set_icu_object_in_cache(ICUObjectCacheType cache_type,
std::shared_ptr<icu::UMemory> obj) {
icu_object_cache_[cache_type] = obj;
void Isolate::set_icu_object_in_cache(
ICUObjectCacheType cache_type, Handle<Object> locales_obj,
std::shared_ptr<icu::UMemory> icu_formatter) {
std::string locale = GetStringFromLocale(locales_obj);
ICUCachePair pair = std::make_pair(locale, icu_formatter);
auto it = icu_object_cache_.find(cache_type);
if (it == icu_object_cache_.end()) {
icu_object_cache_.insert({cache_type, pair});
} else {
it->second = pair;
}
}
void Isolate::clear_cached_icu_object(ICUObjectCacheType cache_type) {
......
......@@ -1263,8 +1263,10 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
kDefaultCollator, kDefaultNumberFormat, kDefaultSimpleDateFormat,
kDefaultSimpleDateFormatForTime, kDefaultSimpleDateFormatForDate};
icu::UMemory* get_cached_icu_object(ICUObjectCacheType cache_type);
icu::UMemory* get_cached_icu_object(ICUObjectCacheType cache_type,
Handle<Object> locales);
void set_icu_object_in_cache(ICUObjectCacheType cache_type,
Handle<Object> locale,
std::shared_ptr<icu::UMemory> obj);
void clear_cached_icu_object(ICUObjectCacheType cache_type);
void ClearCachedIcuObjects();
......@@ -1818,10 +1820,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
return static_cast<std::size_t>(a);
}
};
std::unordered_map<ICUObjectCacheType, std::shared_ptr<icu::UMemory>,
ICUObjectCacheTypeHash>
typedef std::pair<std::string, std::shared_ptr<icu::UMemory>> ICUCachePair;
std::unordered_map<ICUObjectCacheType, ICUCachePair, ICUObjectCacheTypeHash>
icu_object_cache_;
#endif // V8_INTL_SUPPORT
// true if being profiled. Causes collection of extra compile info.
......
......@@ -1014,16 +1014,16 @@ MaybeHandle<String> Intl::StringLocaleConvertCase(Isolate* isolate,
MaybeHandle<Object> Intl::StringLocaleCompare(
Isolate* isolate, Handle<String> string1, Handle<String> string2,
Handle<Object> locales, Handle<Object> options, const char* method) {
// We only cache the instance when both locales and options are undefined,
// as that is the only case when the specified side-effects of examining
// those arguments are unobservable.
bool can_cache =
locales->IsUndefined(isolate) && options->IsUndefined(isolate);
// We only cache the instance when locales is a string/undefined and
// options is undefined, as that is the only case when the specified
// side-effects of examining those arguments are unobservable.
bool can_cache = (locales->IsString() || locales->IsUndefined(isolate)) &&
options->IsUndefined(isolate);
if (can_cache) {
// Both locales and options are undefined, check the cache.
icu::Collator* cached_icu_collator =
static_cast<icu::Collator*>(isolate->get_cached_icu_object(
Isolate::ICUObjectCacheType::kDefaultCollator));
Isolate::ICUObjectCacheType::kDefaultCollator, locales));
// We may use the cached icu::Collator for a fast path.
if (cached_icu_collator != nullptr) {
return Intl::CompareStrings(isolate, *cached_icu_collator, string1,
......@@ -1042,7 +1042,7 @@ MaybeHandle<Object> Intl::StringLocaleCompare(
New<JSCollator>(isolate, constructor, locales, options, method), Object);
if (can_cache) {
isolate->set_icu_object_in_cache(
Isolate::ICUObjectCacheType::kDefaultCollator,
Isolate::ICUObjectCacheType::kDefaultCollator, locales,
std::static_pointer_cast<icu::UMemory>(collator->icu_collator().get()));
}
icu::Collator* icu_collator = collator->icu_collator().raw();
......@@ -1104,16 +1104,16 @@ MaybeHandle<String> Intl::NumberToLocaleString(Isolate* isolate,
ASSIGN_RETURN_ON_EXCEPTION(isolate, numeric_obj,
Object::ToNumeric(isolate, num), String);
// We only cache the instance when both locales and options are undefined,
// as that is the only case when the specified side-effects of examining
// those arguments are unobservable.
bool can_cache =
locales->IsUndefined(isolate) && options->IsUndefined(isolate);
// We only cache the instance when locales is a string/undefined and
// options is undefined, as that is the only case when the specified
// side-effects of examining those arguments are unobservable.
bool can_cache = (locales->IsString() || locales->IsUndefined(isolate)) &&
options->IsUndefined(isolate);
if (can_cache) {
icu::number::LocalizedNumberFormatter* cached_number_format =
static_cast<icu::number::LocalizedNumberFormatter*>(
isolate->get_cached_icu_object(
Isolate::ICUObjectCacheType::kDefaultNumberFormat));
Isolate::ICUObjectCacheType::kDefaultNumberFormat, locales));
// We may use the cached icu::NumberFormat for a fast path.
if (cached_number_format != nullptr) {
return JSNumberFormat::FormatNumeric(isolate, *cached_number_format,
......@@ -1134,7 +1134,7 @@ MaybeHandle<String> Intl::NumberToLocaleString(Isolate* isolate,
if (can_cache) {
isolate->set_icu_object_in_cache(
Isolate::ICUObjectCacheType::kDefaultNumberFormat,
Isolate::ICUObjectCacheType::kDefaultNumberFormat, locales,
std::static_pointer_cast<icu::UMemory>(
number_format->icu_number_formatter().get()));
}
......
......@@ -770,16 +770,16 @@ MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime(
return factory->Invalid_Date_string();
}
// We only cache the instance when both locales and options are undefined,
// as that is the only case when the specified side-effects of examining
// those arguments are unobservable.
bool can_cache =
locales->IsUndefined(isolate) && options->IsUndefined(isolate);
// We only cache the instance when locales is a string/undefined and
// options is undefined, as that is the only case when the specified
// side-effects of examining those arguments are unobservable.
bool can_cache = (locales->IsString() || locales->IsUndefined(isolate)) &&
options->IsUndefined(isolate);
if (can_cache) {
// Both locales and options are undefined, check the cache.
icu::SimpleDateFormat* cached_icu_simple_date_format =
static_cast<icu::SimpleDateFormat*>(
isolate->get_cached_icu_object(cache_type));
isolate->get_cached_icu_object(cache_type, locales));
if (cached_icu_simple_date_format != nullptr) {
return FormatDateTime(isolate, *cached_icu_simple_date_format, x);
}
......@@ -807,8 +807,9 @@ MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime(
if (can_cache) {
isolate->set_icu_object_in_cache(
cache_type, std::static_pointer_cast<icu::UMemory>(
date_time_format->icu_simple_date_format().get()));
cache_type, locales,
std::static_pointer_cast<icu::UMemory>(
date_time_format->icu_simple_date_format().get()));
}
// 5. Return FormatDateTime(dateFormat, x).
icu::SimpleDateFormat* format =
......
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