Skip to content
This repository was archived by the owner on Oct 15, 2020. It is now read-only.

Commit 230ecbd

Browse files
chakrabotkfarnung
authored andcommitted
[Merge chakra-core/ChakraCore@c6b3f201b1] [MERGE #3751 @obastemur] perf: Improve JSON.stringify performance
Merge pull request #3751 from obastemur:fjs Kraken/json-stringify-* perf ~25% better. Acme Air - LTO gain ~1.5% PR Details: - Improve `replacer != function && !HasObjectArray` case. (most common use of JSON.stringify) - Add JSON.stringify test cases for ObjectArray, toJSON, and replacer function - IsNumericPropertyId: fast path for internal properties - use requestContext instead of instance->scriptContext - use wmemcpy instead of memcpy
1 parent 2a7d89c commit 230ecbd

File tree

12 files changed

+220
-84
lines changed

12 files changed

+220
-84
lines changed

deps/chakrashim/core/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ tags
9292
Makefile
9393
pal/src/config.h
9494
DbgController.js.h
95+
lib/wabt/built/config.h
9596

9697
# Generated by other tools
9798
*.lldb.cmd

deps/chakrashim/core/lib/Common/Core/Api.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
44
//-------------------------------------------------------------------------------------------------------
55
#include "CommonCorePch.h"
6+
#ifdef _WIN32
7+
#include <wchar.h> // wmemcpy_s
8+
#endif
69

710
void __stdcall js_memcpy_s(__bcount(sizeInBytes) void *dst, size_t sizeInBytes, __in_bcount(count) const void *src, size_t count)
811
{
@@ -21,8 +24,7 @@ void __stdcall js_wmemcpy_s(__ecount(sizeInWords) char16 *dst, size_t sizeInWord
2124
{
2225
Js::Throw::FatalInternalError();
2326
}
24-
25-
memcpy(dst, src, count * sizeof(char16));
27+
wmemcpy_s(dst, count, src, count);
2628
}
2729

2830
#if defined(_M_IX86) || defined(_M_X64)

deps/chakrashim/core/lib/Runtime/Base/ThreadContext.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,11 @@ void ThreadContext::AddBuiltInPropertyRecord(const Js::PropertyRecord *propertyR
12351235

12361236
BOOL ThreadContext::IsNumericPropertyId(Js::PropertyId propertyId, uint32* value)
12371237
{
1238+
if (Js::IsInternalPropertyId(propertyId))
1239+
{
1240+
return false;
1241+
}
1242+
12381243
Js::PropertyRecord const * propertyRecord = this->GetPropertyName(propertyId);
12391244
Assert(propertyRecord != nullptr);
12401245
if (propertyRecord == nullptr || !propertyRecord->IsNumeric())

deps/chakrashim/core/lib/Runtime/Library/JSON.cpp

Lines changed: 117 additions & 52 deletions
Large diffs are not rendered by default.

deps/chakrashim/core/lib/Runtime/Library/JSON.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ namespace JSON
6161
}
6262
void CompleteInit(Js::Var space, ArenaAllocator* alloc);
6363

64-
Js::Var Str(Js::JavascriptString* key, Js::PropertyId keyId, Js::Var holder);
64+
Js::Var Str(Js::JavascriptString* key, Js::PropertyId keyId, Js::Var holder, Js::Var value = nullptr);
6565
Js::Var Str(uint32 index, Js::Var holder);
6666

6767
private:
@@ -74,8 +74,10 @@ namespace JSON
7474
Js::JavascriptString* GetPropertySeparator();
7575
Js::JavascriptString* GetIndentString(uint count);
7676
Js::JavascriptString* GetMemberSeparator(Js::JavascriptString* indentString);
77-
void StringifyMemberObject( Js::JavascriptString* propertyName, Js::PropertyId id, Js::Var value, Js::ConcatStringBuilder* result,
78-
Js::JavascriptString* &indentString, Js::JavascriptString* &memberSeparator, bool &isFirstMember, bool &isEmpty );
77+
void StringifyMemberObject(Js::JavascriptString* propertyName, Js::PropertyId id,
78+
Js::Var value, Js::ConcatStringBuilder* result, Js::JavascriptString* &indentString,
79+
Js::JavascriptString* &memberSeparator, bool &isFirstMember, bool &isEmpty,
80+
Js::Var propertyValue = nullptr );
7981

8082
uint32 GetPropertyCount(Js::RecyclableObject* object, Js::JavascriptStaticEnumerator* enumerator);
8183
uint32 GetPropertyCount(Js::RecyclableObject* object, Js::JavascriptStaticEnumerator* enumerator, bool* isPrecise);

deps/chakrashim/core/lib/Runtime/Types/DynamicObject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ namespace Js
121121
// For boxing stack instance
122122
DynamicObject(DynamicObject * instance);
123123

124-
DynamicTypeHandler * GetTypeHandler() const;
125124
uint16 GetOffsetOfInlineSlots() const;
126125

127126
template <class T>
@@ -136,6 +135,8 @@ namespace Js
136135
void EnsureSlots(int oldCount, int newCount, ScriptContext * scriptContext, DynamicTypeHandler * newTypeHandler = nullptr);
137136
void EnsureSlots(int newCount, ScriptContext *scriptContext);
138137

138+
DynamicTypeHandler * GetTypeHandler() const;
139+
139140
Var GetSlot(int index);
140141
Var GetInlineSlot(int index);
141142
Var GetAuxSlot(int index);

deps/chakrashim/core/lib/Runtime/Types/PathTypeHandler.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,7 @@ namespace Js
223223

224224
// Check numeric propertyId only if objectArray available
225225
uint32 indexVal;
226-
ScriptContext* scriptContext = instance->GetScriptContext();
227-
if (instance->HasObjectArray() && scriptContext->IsNumericPropertyId(propertyId, &indexVal))
226+
if (instance->HasObjectArray() && requestContext->IsNumericPropertyId(propertyId, &indexVal))
228227
{
229228
return PathTypeHandlerBase::GetItem(instance, originalInstance, indexVal, value, requestContext);
230229
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
7+
var TEST = function(a, b) {
8+
if (a != b) {
9+
throw new Error(a + " != " + b);
10+
}
11+
}
12+
13+
var obj = { str:6 };
14+
obj[0] = 'value0'
15+
obj[6] = 'value6';
16+
TEST(JSON.stringify(obj, function(k, v) {
17+
if (!k) return v;
18+
return v + 1
19+
}), '{"0":"value01","6":"value61","str":7}');
20+
21+
// test ObjectArray
22+
TEST(JSON.stringify({0:0, 1:1, "two":2}), '{"0":0,"1":1,"two":2}')
23+
24+
var a = new Object();
25+
26+
function replacer(k, v)
27+
{
28+
return v;
29+
}
30+
31+
var until = (!WScript.Platform || WScript.Platform.BUILD_TYPE == 'Debug') ? 12 : 1290;
32+
for (var i = 0; i < until; i++)
33+
{
34+
a[i + 10] = 0;
35+
}
36+
37+
TEST(JSON.stringify(a, replacer).substring(0,20), '{"10":0,"11":0,"12":');
38+
39+
console.log("PASS")

deps/chakrashim/core/test/JSON/rlexe.xml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@
2424
</test>
2525
<test>
2626
<default>
27-
<files>stringify-replacerfunc.js</files>
28-
<baseline>stringify-replacerfunc.baseline</baseline>
29-
<compile-flags>-recyclerstress</compile-flags>
30-
<tags>exclude_fre,Slow</tags>
27+
<files>replacerFunction.js</files>
3128
<timeout>300</timeout>
3229
</default>
3330
</test>
@@ -95,4 +92,9 @@
9592
<baseline>syntaxError.baseline</baseline>
9693
</default>
9794
</test>
95+
<test>
96+
<default>
97+
<files>toJSON.js</files>
98+
</default>
99+
</test>
98100
</regress-exe>

deps/chakrashim/core/test/JSON/stringify-replacerfunc.baseline

Lines changed: 0 additions & 1 deletion
This file was deleted.

deps/chakrashim/core/test/JSON/stringify-replacerfunc.js

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
7+
var TEST = function(a, b) {
8+
if (a != b) {
9+
throw new Error(a + " != " + b);
10+
}
11+
}
12+
13+
var fnc = function(n) { this.number = n };
14+
fnc.prototype.toJSON = function() {
15+
return this.number.toString();
16+
}
17+
18+
// test - function prototype new instance
19+
TEST("\"1\"", JSON.stringify(new fnc(1)))
20+
21+
// test - pre-post alter Date toJSON definition
22+
var dateString = JSON.stringify(new Date(0));
23+
TEST("1970", dateString.substr(dateString.indexOf("1970"), 4))
24+
25+
Date.prototype.toJSON = 1;
26+
TEST("{}", JSON.stringify(new Date(0)))
27+
28+
delete Date.prototype.toJSON
29+
TEST("{}", JSON.stringify(new Date(0)))
30+
31+
// test - use from Object prototype
32+
Object.prototype.toJSON = function() { return 2; }
33+
delete fnc.prototype.toJSON;
34+
TEST(2, JSON.stringify(new fnc(1)))
35+
36+
// test - symbol
37+
Object.prototype[Symbol("toJSON")] = function() { return 3; }
38+
TEST(2, JSON.stringify(new fnc(1)))
39+
40+
console.log("PASS")

0 commit comments

Comments
 (0)