Skip to content

Commit

Permalink
Cherry-pick 35a17ac. rdar://122590409
Browse files Browse the repository at this point in the history
    [JSC] Accelerate JSONAtomStringCache
    https://bugs.webkit.org/show_bug.cgi?id=269027
    rdar://122590409

    Reviewed by Mark Lam.

    This patch makes JSON parsing faster by embedding small string content itself into the cache.
    AtomString is stored in the per-thread hash table. And to get that, we need to do hash-table lookup, which is costly.
    These cache can avoid doing that. But still, to check the cache validity, we are still accessing to the string content
    of the AtomString. While the input string content is almost always already in CPU cache since we created this input string,
    AtomString content is very unlikely in the CPU cache. So if we can put this content in much more CPU friendly place, we can
    avoid cache miss much.

    In this patch, we leverage the fact that this cache only stores very small strings. So instead of using content inside AtomString,
    we also copy the string content into the cache's slot itself. So string comparison does not encounter cache miss and accelerate
    the lookup performance. Good part of AtomString is that, after getting this pointer, we rarely access to the string content of AtomString,
    so now, we can avoid access to this string content in majority of cases.

    * Source/JavaScriptCore/runtime/JSONAtomStringCache.h:
    (JSC::JSONAtomStringCache::makeIdentifier):
    (JSC::JSONAtomStringCache::clear):
    (JSC::JSONAtomStringCache::cacheSlot):
    (JSC::JSONAtomStringCache::cache): Deleted.
    * Source/JavaScriptCore/runtime/JSONAtomStringCacheInlines.h:
    (JSC::JSONAtomStringCache::make):
    * Source/WTF/wtf/text/StringCommon.h:

    Canonical link: https://commits.webkit.org/274348@main

Identifier: 272448.549@safari-7618.1.15.11-branch
  • Loading branch information
Constellation authored and Dan Robson committed Feb 13, 2024
1 parent 73b4e47 commit 73b6b77
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 20 deletions.
30 changes: 16 additions & 14 deletions Source/JavaScriptCore/runtime/JSONAtomStringCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,42 +33,44 @@ class VM;

class JSONAtomStringCache {
public:
static constexpr auto maxStringLengthForCache = 32;
static constexpr auto capacity = 512;
using Cache = std::array<RefPtr<AtomStringImpl>, capacity>;
static constexpr auto maxStringLengthForCache = 27;
static constexpr auto capacity = 256;

enum class Type : bool { Identifier };
static constexpr unsigned numberOfTypes = 1;
struct Slot {
UChar m_buffer[maxStringLengthForCache] { };
UChar m_length { 0 };
RefPtr<AtomStringImpl> m_impl;
};
static_assert(sizeof(Slot) <= 64);

using Cache = std::array<Slot, capacity>;

template<typename CharacterType>
ALWAYS_INLINE Ref<AtomStringImpl> makeIdentifier(const CharacterType* characters, unsigned length)
{
return make(Type::Identifier, characters, length);
return make(characters, length);
}

ALWAYS_INLINE void clear()
{
for (unsigned i = 0; i < numberOfTypes; ++i)
cache(static_cast<Type>(i)).fill({ });
m_cache.fill({ });
}

VM& vm() const;

private:
template<typename CharacterType>
Ref<AtomStringImpl> make(Type, const CharacterType*, unsigned length);
Ref<AtomStringImpl> make(const CharacterType*, unsigned length);

ALWAYS_INLINE RefPtr<AtomStringImpl>& cacheSlot(Type type, UChar firstCharacter, UChar lastCharacter, UChar length)
ALWAYS_INLINE Slot& cacheSlot(UChar firstCharacter, UChar lastCharacter, UChar length)
{
unsigned hash = (firstCharacter << 6) ^ ((lastCharacter << 14) ^ firstCharacter);
hash += (hash >> 14) + (length << 14);
hash ^= hash << 14;
return cache(type)[(hash + (hash >> 6)) % capacity];
return m_cache[(hash + (hash >> 6)) % capacity];
}

ALWAYS_INLINE Cache& cache(Type type) { return m_caches[static_cast<size_t>(type)]; }

Cache m_caches[numberOfTypes] { };
Cache m_cache { };
};

} // namespace JSC
14 changes: 8 additions & 6 deletions Source/JavaScriptCore/runtime/JSONAtomStringCacheInlines.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
namespace JSC {

template<typename CharacterType>
ALWAYS_INLINE Ref<AtomStringImpl> JSONAtomStringCache::make(Type type, const CharacterType* characters, unsigned length)
ALWAYS_INLINE Ref<AtomStringImpl> JSONAtomStringCache::make(const CharacterType* characters, unsigned length)
{
if (!length)
return *static_cast<AtomStringImpl*>(StringImpl::empty());
Expand All @@ -42,18 +42,20 @@ ALWAYS_INLINE Ref<AtomStringImpl> JSONAtomStringCache::make(Type type, const Cha
if (length == 1) {
if (firstCharacter <= maxSingleCharacterString)
return vm().smallStrings.singleCharacterStringRep(firstCharacter);
} else if (length > maxStringLengthForCache)
} else if (UNLIKELY(length > maxStringLengthForCache))
return AtomStringImpl::add(characters, length).releaseNonNull();

auto lastCharacter = characters[length - 1];
auto& slot = cacheSlot(type, firstCharacter, lastCharacter, length);
if (!equal(slot.get(), characters, length)) {
auto& slot = cacheSlot(firstCharacter, lastCharacter, length);
if (UNLIKELY(slot.m_length != length || !equal(slot.m_buffer, characters, length))) {
auto result = AtomStringImpl::add(characters, length);
slot = result;
slot.m_impl = result;
slot.m_length = length;
WTF::copyElements(slot.m_buffer, characters, length);
return result.releaseNonNull();
}

return *slot;
return *slot.m_impl;
}

ALWAYS_INLINE VM& JSONAtomStringCache::vm() const
Expand Down
10 changes: 10 additions & 0 deletions Source/WTF/wtf/text/StringCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,16 @@ inline void copyElements(uint8_t* __restrict destination, const uint64_t* __rest
*destination++ = *source++;
}

inline void copyElements(UChar* __restrict destination, const LChar* __restrict source, size_t length)
{
copyElements(bitwise_cast<uint16_t*>(destination), bitwise_cast<const uint8_t*>(source), length);
}

inline void copyElements(LChar* __restrict destination, const UChar* __restrict source, size_t length)
{
copyElements(bitwise_cast<uint8_t*>(destination), bitwise_cast<const uint16_t*>(source), length);
}

}

using WTF::equalIgnoringASCIICase;
Expand Down

0 comments on commit 73b6b77

Please sign in to comment.