Skip to content

Commit 8967b64

Browse files
committed
[MERGE #4907 @sethbrenith] Cache property enumeration in JSON.stringify like we do for Object.assign
Merge pull request #4907 from sethbrenith:user/sethb/stringify-cache-iter JSON payloads tend to be relatively repetitive, so we can save a lot on large serializations by reusing object property iteration. On a microbenchmark that serializes and parses a repetitive piece of data that I found in Speedometer, I see a 35% improvement.
2 parents d8a28b6 + 8b2536f commit 8967b64

File tree

3 files changed

+21
-6
lines changed

3 files changed

+21
-6
lines changed

lib/Runtime/Library/JSONStringifier.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,8 @@ JSONStringifier::ReadObject(_In_ RecyclableObject* obj, _In_ JSONObjectStack* ob
548548
else
549549
{
550550
JavascriptStaticEnumerator enumerator;
551-
if (obj->GetEnumerator(&enumerator, EnumeratorFlags::SnapShotSemantics | EnumeratorFlags::EphemeralReference, this->scriptContext))
551+
EnumeratorCache* cache = this->scriptContext->GetLibrary()->GetStringifyCache(obj->GetType());
552+
if (obj->GetEnumerator(&enumerator, EnumeratorFlags::SnapShotSemantics | EnumeratorFlags::EphemeralReference | EnumeratorFlags::UseCache, this->scriptContext, cache))
552553
{
553554
JavascriptString* propertyName = nullptr;
554555
PropertyId nextKey = Constants::NoProperty;

lib/Runtime/Library/JavascriptLibrary.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6369,15 +6369,25 @@ namespace Js
63696369
}
63706370

63716371
EnumeratorCache* JavascriptLibrary::GetObjectAssignCache(Type* type)
6372+
{
6373+
return GetEnumeratorCache<Cache::AssignCacheSize>(type, &this->cache.assignCache);
6374+
}
6375+
6376+
EnumeratorCache* JavascriptLibrary::GetStringifyCache(Type* type)
6377+
{
6378+
return GetEnumeratorCache<Cache::StringifyCacheSize>(type, &this->cache.stringifyCache);
6379+
}
6380+
6381+
template<uint cacheSlotCount> EnumeratorCache* JavascriptLibrary::GetEnumeratorCache(Type* type, Field(EnumeratorCache*)* cacheSlots)
63726382
{
63736383
// Size must be power of 2 for cache indexing to work
6374-
CompileAssert((Cache::AssignCacheSize & (Cache::AssignCacheSize - 1)) == 0);
6384+
CompileAssert((cacheSlotCount & (cacheSlotCount - 1)) == 0);
63756385

6376-
if (this->cache.assignCache == nullptr)
6386+
if (*cacheSlots == nullptr)
63776387
{
6378-
this->cache.assignCache = AllocatorNewArrayZ(CacheAllocator, scriptContext->GetEnumeratorAllocator(), EnumeratorCache, Cache::AssignCacheSize);
6388+
*cacheSlots = AllocatorNewArrayZ(CacheAllocator, scriptContext->GetEnumeratorAllocator(), EnumeratorCache, cacheSlotCount);
63796389
}
6380-
return &this->cache.assignCache[(((uintptr_t)type) >> PolymorphicInlineCacheShift) & (Cache::AssignCacheSize - 1)];
6390+
return &(*cacheSlots)[(((uintptr_t)type) >> PolymorphicInlineCacheShift) & (cacheSlotCount - 1)];
63816391
}
63826392

63836393
SymbolCacheMap* JavascriptLibrary::EnsureSymbolMap()

lib/Runtime/Library/JavascriptLibrary.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ namespace Js
7070
struct Cache
7171
{
7272
static const uint AssignCacheSize = 16;
73+
static const uint StringifyCacheSize = 16;
7374

7475
Field(PropertyStringMap*) propertyStrings[80];
7576
Field(JavascriptString *) lastNumberToStringRadix10String;
@@ -89,12 +90,13 @@ namespace Js
8990
Field(ScriptContextPolymorphicInlineCache*) toStringTagCache;
9091
Field(ScriptContextPolymorphicInlineCache*) toJSONCache;
9192
Field(EnumeratorCache*) assignCache;
93+
Field(EnumeratorCache*) stringifyCache;
9294
#if ENABLE_PROFILE_INFO
9395
#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
9496
Field(DynamicProfileInfoList*) profileInfoList;
9597
#endif
9698
#endif
97-
Cache() : toStringTagCache(nullptr), toJSONCache(nullptr), assignCache(nullptr) { }
99+
Cache() : toStringTagCache(nullptr), toJSONCache(nullptr), assignCache(nullptr), stringifyCache(nullptr) { }
98100
};
99101

100102
class MissingPropertyTypeHandler;
@@ -1176,6 +1178,7 @@ namespace Js
11761178
}
11771179

11781180
EnumeratorCache* GetObjectAssignCache(Type* type);
1181+
EnumeratorCache* GetStringifyCache(Type* type);
11791182

11801183
bool GetArrayObjectHasUserDefinedSpecies() const { return arrayObjectHasUserDefinedSpecies; }
11811184
void SetArrayObjectHasUserDefinedSpecies(bool val) { arrayObjectHasUserDefinedSpecies = val; }
@@ -1296,6 +1299,7 @@ namespace Js
12961299
void AddMember(DynamicObject* object, PropertyId propertyId, Var value, PropertyAttributes attributes);
12971300
JavascriptString* CreateEmptyString();
12981301

1302+
template<uint cacheSlotCount> EnumeratorCache* GetEnumeratorCache(Type* type, Field(EnumeratorCache*)* cacheSlots);
12991303

13001304
static bool __cdecl InitializeGeneratorFunction(DynamicObject* function, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode);
13011305

0 commit comments

Comments
 (0)