source: webkit/trunk/Source/JavaScriptCore/dynbench.cpp

Last change on this file was 292118, checked in by Chris Dumez, 3 years ago

Optimize the construction of a JSC::Identifier from an ASCIILiteral
https://bugs.webkit.org/show_bug.cgi?id=238552

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

There are two ways to construct a JSC::Identifier from a string literal and both
were sub-optimal:

  • template<unsigned charactersCount> static Identifier fromString(VM&, const char (&characters)[charactersCount]): Even though it knows the charactersCount at compile time, it doesn't leverage this information at all. Also, it ends up calling AtomStringImpl::add() instead of AtomStringImpl::addLiteral() which means we lose the knowledge that it was a string literal and the optimization that come with it (e.g. not copying the characters over). In this patch, I am deprecating this function in favor of fromString(ASCIILiteral) which is now more efficient. I'll remove it in a follow up.
  • static Identifier fromString(VM&, ASCIILiteral): It ended up calling the Identifier(VM&, String) constructor which meant that we were potentially constructing a String/StringImpl unnecessarily before atomizing it since the string may already be in the AtomString table. We also failed to use the smallStrings cache for literals whose length is 1, which would happen when calling fromString(VM&, const char (&characters)[charactersCount]).

In this patch, I optimized fromString(VM&, ASCIILiteral) to leverage the smallStrings cache
when necessary and call AtomStringImpl::addLiteral() instead of AtomStringImpl::add(). I also
made sure to mark fromString(VM&, ASCIILiteral) and Identifier(VM&, ASCIILiteral) as
ALWAYS_INLINE to make sure the compiler can optimize out the strlen() calls.

According to A/B bots, this is a 1-1.5% progression on Speedometer and and a 1.3% progression on
JetStream on iMac 20,1 (Intel). Sadly, this is perf-neutral on those two benchmarks on Apple
Silicon.

  • API/JSAPIGlobalObject.mm:

(JSC::JSAPIGlobalObject::moduleLoaderCreateImportMetaProperties):

  • API/JSBase.cpp:

(JSGetMemoryUsageStatistics):

  • API/JSObjectRef.cpp:

(JSObjectMakeFunction):

  • builtins/BuiltinNames.cpp:

(JSC::BuiltinNames::BuiltinNames):

  • builtins/BuiltinUtils.h:
  • dynbench.cpp:
  • inspector/JSInjectedScriptHost.cpp:

(Inspector::JSInjectedScriptHost::functionDetails):
(Inspector::constructInternalProperty):
(Inspector::JSInjectedScriptHost::weakMapEntries):
(Inspector::JSInjectedScriptHost::weakSetEntries):
(Inspector::JSInjectedScriptHost::iteratorEntries):

  • inspector/JSJavaScriptCallFrame.cpp:

(Inspector::valueForScopeLocation):
(Inspector::JSJavaScriptCallFrame::scopeDescriptions):

  • inspector/ScriptCallStackFactory.cpp:

(Inspector::extractSourceInformationFromException):

  • inspector/agents/InspectorAuditAgent.cpp:

(Inspector::InspectorAuditAgent::populateAuditObject):

  • jsc.cpp:

(GlobalObject::moduleLoaderCreateImportMetaProperties):
(JSC_DEFINE_HOST_FUNCTION):
(dumpException):

  • runtime/AbstractModuleRecord.cpp:

(JSC::AbstractModuleRecord::hostResolveImportedModule):

  • runtime/AtomicsObject.cpp:

(JSC::AtomicsObject::finishCreation):

  • runtime/CommonIdentifiers.cpp:

(JSC::CommonIdentifiers::CommonIdentifiers):

  • runtime/FinalizationRegistryPrototype.cpp:

(JSC::FinalizationRegistryPrototype::finishCreation):

  • runtime/Identifier.cpp:

(JSC::Identifier::addLiteral):

  • runtime/Identifier.h:

(JSC::Identifier::Identifier):

  • runtime/IdentifierInlines.h:

(JSC::Identifier::fromString):

  • runtime/IntlLocale.cpp:

(JSC::IntlLocale::textInfo):
(JSC::IntlLocale::weekInfo):

  • runtime/IntlNumberFormat.cpp:

(JSC::IntlNumberFormat::initializeNumberFormat):
(JSC::IntlNumberFormat::resolvedOptions const):
(JSC::IntlNumberFormat::formatToPartsInternal):

  • runtime/IntlPluralRules.cpp:

(JSC::IntlPluralRules::resolvedOptions const):

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::exposeDollarVM):

  • runtime/JSModuleLoader.cpp:

(JSC::JSModuleLoader::finishCreation):

  • runtime/MathObject.cpp:

(JSC::MathObject::finishCreation):

  • runtime/NumberConstructor.cpp:

(JSC::NumberConstructor::finishCreation):

  • runtime/StringPrototype.cpp:

(JSC::StringPrototype::finishCreation):

  • runtime/SymbolConstructor.cpp:
  • tools/JSDollarVM.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):
(JSC::JSDollarVM::finishCreation):

  • wasm/js/JSWebAssemblyGlobal.cpp:

(JSC::JSWebAssemblyGlobal::type):

  • wasm/js/JSWebAssemblyMemory.cpp:

(JSC::JSWebAssemblyMemory::type):

  • wasm/js/JSWebAssemblyTable.cpp:

(JSC::JSWebAssemblyTable::type):

  • wasm/js/WebAssemblyGlobalConstructor.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):

  • wasm/js/WebAssemblyMemoryConstructor.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):

  • wasm/js/WebAssemblyModuleConstructor.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):

  • wasm/js/WebAssemblyTableConstructor.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):

  • wasm/js/WebAssemblyTagPrototype.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):

Source/WebCore:

Adopt the JSC::Identifier constructor from an ASCIILiteral more widely.

  • Modules/applepay-ams-ui/ApplePayAMSUIPaymentHandler.cpp:

(WebCore::ApplePayAMSUIPaymentHandler::finishSession):

  • Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.cpp:

(WebCore::CDMSessionClearKey::update):

  • Modules/webaudio/AudioWorkletGlobalScope.cpp:

(WebCore::AudioWorkletGlobalScope::registerProcessor):

  • Modules/webaudio/AudioWorkletProcessor.cpp:

(WebCore::AudioWorkletProcessor::process):

  • bindings/js/JSCustomElementRegistryCustom.cpp:

(WebCore::JSCustomElementRegistry::define):

  • bindings/js/JSDOMWindowCustom.cpp:

(WebCore::DialogHandler::dialogCreated):
(WebCore::DialogHandler::returnValue const):
(WebCore::JSDOMWindow::setOpener):
(WebCore::JSDOMWindow::setOpenDatabase):

  • bindings/js/JSEventListener.cpp:

(WebCore::JSEventListener::handleEvent):

  • bindings/js/JSImageDataCustom.cpp:

(WebCore::toJSNewlyCreated):

  • bindings/js/ScriptModuleLoader.cpp:

(WebCore::ScriptModuleLoader::createImportMetaProperties):

  • bindings/scripts/CodeGeneratorJS.pm:

(GenerateDictionaryImplementationContent):
(addUnscopableProperties):
(GenerateImplementation):
(GenerateAttributeSetterBodyDefinition):
(GenerateDefaultToJSONOperationDefinition):
(GenerateCallbackImplementationContent):
(GenerateConstructorHelperMethods):

  • bindings/scripts/test/JS/JSExposedStar.cpp:

(WebCore::JSExposedStarPrototype::finishCreation):

  • bindings/scripts/test/JS/JSExposedToWorkerAndWindow.cpp:

(WebCore::convertDictionary<ExposedToWorkerAndWindow::Dict>):
(WebCore::convertDictionaryToJS):

  • bindings/scripts/test/JS/JSTestCEReactions.cpp:

(WebCore::setJSTestCEReactions_stringifierAttributeSetter):
(WebCore::setJSTestCEReactions_stringifierAttributeNotNeededSetter):

  • bindings/scripts/test/JS/JSTestCallbackInterface.cpp:

(WebCore::convertDictionary<TestCallbackInterface::Dictionary>):
(WebCore::JSTestCallbackInterface::callbackWithNoParam):
(WebCore::JSTestCallbackInterface::callbackWithArrayParam):
(WebCore::JSTestCallbackInterface::callbackWithSerializedScriptValueParam):
(WebCore::JSTestCallbackInterface::callbackWithStringList):
(WebCore::JSTestCallbackInterface::callbackWithBoolean):
(WebCore::JSTestCallbackInterface::callbackRequiresThisToPass):
(WebCore::JSTestCallbackInterface::callbackWithAReturnValue):
(WebCore::JSTestCallbackInterface::callbackThatRethrowsExceptions):
(WebCore::JSTestCallbackInterface::callbackWithThisObject):

  • bindings/scripts/test/JS/JSTestConditionalIncludes.cpp:

(WebCore::JSTestConditionalIncludesDOMConstructor::initializeProperties):
(WebCore::JSTestConditionalIncludesPrototype::finishCreation):

  • bindings/scripts/test/JS/JSTestConditionallyReadWrite.cpp:

(WebCore::JSTestConditionallyReadWritePrototype::finishCreation):

  • bindings/scripts/test/JS/JSTestDefaultToJSON.cpp:

(WebCore::JSTestDefaultToJSONPrototype::finishCreation):
(WebCore::jsTestDefaultToJSONPrototypeFunction_toJSONBody):

  • bindings/scripts/test/JS/JSTestDefaultToJSONFilteredByExposed.cpp:

(WebCore::JSTestDefaultToJSONFilteredByExposedPrototype::finishCreation):
(WebCore::jsTestDefaultToJSONFilteredByExposedPrototypeFunction_toJSONBody):

  • bindings/scripts/test/JS/JSTestDefaultToJSONInherit.cpp:

(WebCore::jsTestDefaultToJSONInheritPrototypeFunction_toJSONBody):

  • bindings/scripts/test/JS/JSTestDefaultToJSONInheritFinal.cpp:

(WebCore::jsTestDefaultToJSONInheritFinalPrototypeFunction_toJSONBody):

  • bindings/scripts/test/JS/JSTestDerivedDictionary.cpp:

(WebCore::convertDictionary<TestDerivedDictionary>):
(WebCore::convertDictionaryToJS):

  • bindings/scripts/test/JS/JSTestDerivedDictionary2.cpp:

(WebCore::convertDictionary<TestDerivedDictionary2>):
(WebCore::convertDictionaryToJS):
(WebCore::convertDictionary<TestDerivedDictionary2::Dictionary>):

  • bindings/scripts/test/JS/JSTestDictionary.cpp:

(WebCore::convertDictionary<TestDictionary>):

  • bindings/scripts/test/JS/JSTestDictionaryWithOnlyConditionalMembers.cpp:

(WebCore::convertDictionary<TestDictionaryWithOnlyConditionalMembers>):
(WebCore::convertDictionaryToJS):

  • bindings/scripts/test/JS/JSTestEnabledBySetting.cpp:

(WebCore::JSTestEnabledBySettingDOMConstructor::initializeProperties):
(WebCore::JSTestEnabledBySettingPrototype::finishCreation):

  • bindings/scripts/test/JS/JSTestEventConstructor.cpp:

(WebCore::convertDictionary<TestEventConstructor::Init>):

  • bindings/scripts/test/JS/JSTestGenerateIsReachable.cpp:

(WebCore::JSTestGenerateIsReachablePrototype::finishCreation):

  • bindings/scripts/test/JS/JSTestInheritedDictionary.cpp:

(WebCore::convertDictionary<TestInheritedDictionary>):
(WebCore::convertDictionaryToJS):

  • bindings/scripts/test/JS/JSTestInheritedDictionary2.cpp:

(WebCore::convertDictionary<TestInheritedDictionary2>):
(WebCore::convertDictionaryToJS):

  • bindings/scripts/test/JS/JSTestInterface.cpp:

(WebCore::JSTestInterfacePrototype::finishCreation):

  • bindings/scripts/test/JS/JSTestNamespaceObject.cpp:

(WebCore::JSTestNamespaceObjectDOMConstructor::initializeProperties):

  • bindings/scripts/test/JS/JSTestNode.cpp:

(WebCore::JSTestNodePrototype::finishCreation):
(WebCore::jsTestNodePrototypeFunction_toJSONBody):

  • bindings/scripts/test/JS/JSTestObj.cpp:

(WebCore::convertDictionary<TestObj::Dictionary>):
(WebCore::convertDictionaryToJS):
(WebCore::convertDictionary<TestObj::DictionaryThatShouldNotTolerateNull>):
(WebCore::convertDictionary<TestObj::DictionaryThatShouldTolerateNull>):
(WebCore::convertDictionary<AlternateDictionaryName>):
(WebCore::convertDictionary<TestObj::ParentDictionary>):
(WebCore::convertDictionary<TestObj::ChildDictionary>):
(WebCore::convertDictionary<TestObj::ConditionalDictionaryA>):
(WebCore::convertDictionary<TestObj::ConditionalDictionaryB>):
(WebCore::convertDictionary<TestObj::ConditionalDictionaryC>):
(WebCore::JSTestObjDOMConstructor::initializeProperties):
(WebCore::JSTestObjPrototype::finishCreation):
(WebCore::setJSTestObj_putForwardsAttributeSetter):
(WebCore::setJSTestObj_putForwardsNullableAttributeSetter):

  • bindings/scripts/test/JS/JSTestPromiseRejectionEvent.cpp:

(WebCore::convertDictionary<TestPromiseRejectionEvent::Init>):

  • bindings/scripts/test/JS/JSTestStandaloneDictionary.cpp:

(WebCore::convertDictionary<DictionaryImplName>):
(WebCore::convertDictionaryToJS):

  • html/HTMLMediaElement.cpp:

(WebCore::controllerJSValue):
(WebCore::HTMLMediaElement::updateCaptionContainer):
(WebCore::HTMLMediaElement::ensureMediaControlsInjectedScript):
(WebCore::HTMLMediaElement::didAddUserAgentShadowRoot):
(WebCore::HTMLMediaElement::updateMediaControlsAfterPresentationModeChange):
(WebCore::HTMLMediaElement::getCurrentMediaControlsStatus):

File size: 9.3 KB
Line 
1/*
2 * Copyright (C) 2015-2022 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#include "Identifier.h"
29#include "InitializeThreading.h"
30#include "JSCInlines.h"
31#include "JSCJSValue.h"
32#include "JSGlobalObject.h"
33#include "JSLock.h"
34#include "JSObject.h"
35#include "VM.h"
36#include <wtf/MainThread.h>
37#include <wtf/text/StringCommon.h>
38
39using namespace JSC;
40
41namespace {
42
43Lock crashLock;
44const char* nameFilter;
45unsigned requestedIterationCount;
46
47#define CHECK(x) do { \
48 if (!!(x)) \
49 break; \
50 crashLock.lock(); \
51 WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #x); \
52 CRASH(); \
53 } while (false)
54
55template<typename Callback>
56NEVER_INLINE void benchmarkImpl(const char* name, unsigned iterationCount, const Callback& callback)
57{
58 if (nameFilter && WTF::findIgnoringASCIICaseWithoutLength(name, nameFilter) == WTF::notFound)
59 return;
60
61 if (requestedIterationCount)
62 iterationCount = requestedIterationCount;
63
64 MonotonicTime before = MonotonicTime::now();
65 callback(iterationCount);
66 MonotonicTime after = MonotonicTime::now();
67 dataLog(name, ": ", (after - before).milliseconds(), " ms.\n");
68}
69
70} // anonymous namespace
71
72// Use WTF_IGNORES_THREAD_SAFETY_ANALYSIS because the function keeps holding crashLock when returning.
73int main(int argc, char** argv) WTF_IGNORES_THREAD_SAFETY_ANALYSIS
74{
75 if (argc >= 2) {
76 if (argv[1][0] == '-') {
77 dataLog("Usage: dynbench [<filter> [<iteration count>]]\n");
78 return 1;
79 }
80
81 nameFilter = argv[1];
82
83 if (argc >= 3) {
84 if (sscanf(argv[2], "%u", &requestedIterationCount) != 1) {
85 dataLog("Could not parse iteration count ", argv[2], "\n");
86 return 1;
87 }
88 }
89 }
90
91 WTF::initializeMainThread();
92 JSC::initialize();
93
94 VM& vm = VM::create(HeapType::Large).leakRef();
95 {
96 JSLockHolder locker(vm);
97
98 JSGlobalObject* globalObject =
99 JSGlobalObject::create(vm, JSGlobalObject::createStructure(vm, jsNull()));
100
101 Identifier identF = Identifier::fromString(vm, "f"_s);
102 Identifier identG = Identifier::fromString(vm, "g"_s);
103
104 Structure* objectStructure =
105 JSFinalObject::createStructure(vm, globalObject, globalObject->objectPrototype(), 2);
106
107 // Non-strict dynamic get by id:
108 JSValue object = JSFinalObject::create(vm, objectStructure);
109 {
110 PutPropertySlot slot(object, false, PutPropertySlot::PutById);
111 object.putInline(globalObject, identF, jsNumber(42), slot);
112 }
113 {
114 PutPropertySlot slot(object, false, PutPropertySlot::PutById);
115 object.putInline(globalObject, identG, jsNumber(43), slot);
116 }
117 benchmarkImpl(
118 "Non Strict Dynamic Get By Id",
119 1000000,
120 [&] (unsigned iterationCount) {
121 for (unsigned i = iterationCount; i--;) {
122 JSValue result = object.get(globalObject, identF);
123 CHECK(result == jsNumber(42));
124 result = object.get(globalObject, identG);
125 CHECK(result == jsNumber(43));
126 }
127 });
128
129 // Non-strict dynamic put by id replace:
130 object = JSFinalObject::create(vm, objectStructure);
131 {
132 PutPropertySlot slot(object, false, PutPropertySlot::PutById);
133 object.putInline(globalObject, identF, jsNumber(42), slot);
134 }
135 {
136 PutPropertySlot slot(object, false, PutPropertySlot::PutById);
137 object.putInline(globalObject, identG, jsNumber(43), slot);
138 }
139 benchmarkImpl(
140 "Non Strict Dynamic Put By Id Replace",
141 1000000,
142 [&] (unsigned iterationCount) {
143 for (unsigned i = iterationCount; i--;) {
144 {
145 PutPropertySlot slot(object, false, PutPropertySlot::PutById);
146 object.putInline(globalObject, identF, jsNumber(i), slot);
147 }
148 {
149 PutPropertySlot slot(object, false, PutPropertySlot::PutById);
150 object.putInline(globalObject, identG, jsNumber(i), slot);
151 }
152 }
153 });
154
155 // Non-strict dynamic put by id transition with object allocation:
156 benchmarkImpl(
157 "Non Strict Dynamic Allocation and Put By Id Transition",
158 1000000,
159 [&] (unsigned iterationCount) {
160 for (unsigned i = iterationCount; i--;) {
161 JSValue object = JSFinalObject::create(vm, objectStructure);
162 {
163 PutPropertySlot slot(object, false, PutPropertySlot::PutById);
164 object.putInline(globalObject, identF, jsNumber(i), slot);
165 }
166 {
167 PutPropertySlot slot(object, false, PutPropertySlot::PutById);
168 object.putInline(globalObject, identG, jsNumber(i), slot);
169 }
170 }
171 });
172
173 // Non-strict dynamic get by id with dynamic store context:
174 object = JSFinalObject::create(vm, objectStructure);
175 {
176 PutPropertySlot slot(object, false);
177 object.putInline(globalObject, identF, jsNumber(42), slot);
178 }
179 {
180 PutPropertySlot slot(object, false);
181 object.putInline(globalObject, identG, jsNumber(43), slot);
182 }
183 benchmarkImpl(
184 "Non Strict Dynamic Get By Id With Dynamic Store Context",
185 1000000,
186 [&] (unsigned iterationCount) {
187 for (unsigned i = iterationCount; i--;) {
188 JSValue result = object.get(globalObject, identF);
189 CHECK(result == jsNumber(42));
190 result = object.get(globalObject, identG);
191 CHECK(result == jsNumber(43));
192 }
193 });
194
195 // Non-strict dynamic put by id replace with dynamic store context:
196 object = JSFinalObject::create(vm, objectStructure);
197 {
198 PutPropertySlot slot(object, false);
199 object.putInline(globalObject, identF, jsNumber(42), slot);
200 }
201 {
202 PutPropertySlot slot(object, false);
203 object.putInline(globalObject, identG, jsNumber(43), slot);
204 }
205 benchmarkImpl(
206 "Non Strict Dynamic Put By Id Replace With Dynamic Store Context",
207 1000000,
208 [&] (unsigned iterationCount) {
209 for (unsigned i = iterationCount; i--;) {
210 {
211 PutPropertySlot slot(object, false);
212 object.putInline(globalObject, identF, jsNumber(i), slot);
213 }
214 {
215 PutPropertySlot slot(object, false);
216 object.putInline(globalObject, identG, jsNumber(i), slot);
217 }
218 }
219 });
220
221 // Non-strict dynamic put by id transition with object allocation with dynamic store context:
222 benchmarkImpl(
223 "Non Strict Dynamic Allocation and Put By Id Transition With Dynamic Store Context",
224 1000000,
225 [&] (unsigned iterationCount) {
226 for (unsigned i = iterationCount; i--;) {
227 JSValue object = JSFinalObject::create(vm, objectStructure);
228 {
229 PutPropertySlot slot(object, false);
230 object.putInline(globalObject, identF, jsNumber(i), slot);
231 }
232 {
233 PutPropertySlot slot(object, false);
234 object.putInline(globalObject, identG, jsNumber(i), slot);
235 }
236 }
237 });
238 }
239
240 crashLock.lock();
241 return 0;
242}
243
Note: See TracBrowser for help on using the repository browser.