Fine-tune cached array indices on strings

When converting a Smi to a String, we can skip the check for a
cached array index on the result in case of a number-to-string
cache hit. When trying to convert a String back to an index, the
inlined fast path can check for a cached index (in addition to
checking for a cached known negative).
Locally this yields about 5% on the JSTests/Proxies/GetIndex* tests.

Bug: chromium:1028021
Change-Id: I117eae01b1ad9c5d107ad7e598464b96dae9a6b9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1943160
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65299}
This commit is contained in:
Jakob Kummerow 2019-12-02 17:30:30 +01:00 committed by Commit Bot
parent 7ecb124a67
commit f33902c05b
2 changed files with 29 additions and 3 deletions

View File

@ -3589,15 +3589,25 @@ Handle<String> Factory::SmiToString(Smi number, bool check_cache) {
Vector<char> buffer(arr, arraysize(arr));
const char* string = IntToCString(number.value(), buffer);
return NumberToStringCacheSet(handle(number, isolate()), hash, string,
check_cache);
Handle<String> result = NumberToStringCacheSet(handle(number, isolate()),
hash, string, check_cache);
// Compute the hash here (rather than letting the caller take care of it) so
// that the "cache hit" case above doesn't have to bother with it.
STATIC_ASSERT(Smi::kMaxValue <= std::numeric_limits<uint32_t>::max());
if (result->hash_field() == String::kEmptyHashField && number.value() >= 0) {
uint32_t field = StringHasher::MakeArrayIndexHash(
static_cast<uint32_t>(number.value()), result->length());
result->set_hash_field(field);
}
return result;
}
Handle<String> Factory::SizeToString(size_t value, bool check_cache) {
Handle<String> result;
if (value <= Smi::kMaxValue) {
int32_t int32v = static_cast<int32_t>(static_cast<uint32_t>(value));
result = SmiToString(Smi::FromInt(int32v), check_cache);
// SmiToString sets the hash when needed, we can return immediately.
return SmiToString(Smi::FromInt(int32v), check_cache);
} else if (value <= kMaxSafeInteger) {
// TODO(jkummerow): Refactor the cache to not require Objects as keys.
double double_value = static_cast<double>(value);

View File

@ -773,6 +773,14 @@ void StringCharacterStream::VisitTwoByteString(const uint16_t* chars,
bool String::AsArrayIndex(uint32_t* index) {
DisallowHeapAllocation no_gc;
uint32_t field = hash_field();
// The {IsHashFieldComputed} check is not functionally necessary as the
// subsequent mask includes it; it's here to make the logic more obvious,
// and the compile will fold it away so it doesn't hurt performance.
if (IsHashFieldComputed(field) &&
(field & kDoesNotContainCachedArrayIndexMask) == 0) {
*index = ArrayIndexValueBits::decode(field);
return true;
}
if (IsHashFieldComputed(field) && (field & kIsNotArrayIndexMask)) {
return false;
}
@ -781,6 +789,14 @@ bool String::AsArrayIndex(uint32_t* index) {
bool String::AsIntegerIndex(size_t* index) {
uint32_t field = hash_field();
// The {IsHashFieldComputed} check is not functionally necessary as the
// subsequent mask includes it; it's here to make the logic more obvious,
// and the compile will fold it away so it doesn't hurt performance.
if (IsHashFieldComputed(field) &&
(field & kDoesNotContainCachedArrayIndexMask) == 0) {
*index = ArrayIndexValueBits::decode(field);
return true;
}
if (IsHashFieldComputed(field) && (field & kIsNotIntegerIndexMask)) {
return false;
}