Changeset 273718 in webkit
- Timestamp:
- Mar 1, 2021 6:14:51 PM (17 months ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 6 edited
-
ChangeLog (modified) (1 diff)
-
builtins/BuiltinNames.h (modified) (1 diff)
-
builtins/PromiseOperations.js (modified) (8 diffs)
-
runtime/JSGlobalObject.cpp (modified) (1 diff)
-
runtime/JSMicrotask.cpp (modified) (3 diffs)
-
runtime/JSMicrotask.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r273717 r273718 1 2021-03-01 Keith Miller <keith_miller@apple.com> 2 3 Reduce promise reaction memory usage when there are multiple reactions 4 https://bugs.webkit.org/show_bug.cgi?id=222533 5 6 Reviewed by Yusuke Suzuki. 7 8 Previously, we would store each reaction in a linked list. This 9 meant each reaction required 8 bytes to point to the next 10 reaction. Instead, this patch makes it so the first reaction is 11 store inline an object and any additional reactions are store into 12 index storage for that object. This doesn't save memory for the 13 first reaction since we now need to have a count of all the out of 14 line reactions but extra reactions use 8 bytes less each. 15 16 * builtins/BuiltinNames.h: 17 * builtins/PromiseOperations.js: 18 (globalPrivate.pushNewPromiseReaction): 19 (globalPrivate.triggerPromiseReactions): 20 (globalPrivate.newPromiseReaction): Deleted. 21 (globalPrivate.resolvePromise): Deleted. 22 (globalPrivate.rejectPromise): Deleted. 23 (globalPrivate.fulfillPromise): Deleted. 24 (globalPrivate.resolvePromiseWithFirstResolvingFunctionCallCheck): Deleted. 25 (globalPrivate.fulfillPromiseWithFirstResolvingFunctionCallCheck): Deleted. 26 (globalPrivate.rejectPromiseWithFirstResolvingFunctionCallCheck): Deleted. 27 (globalPrivate.createResolvingFunctions): Deleted. 28 (globalPrivate.promiseReactionJobWithoutPromise): Deleted. 29 (globalPrivate.resolveWithoutPromise): Deleted. 30 (globalPrivate.rejectWithoutPromise): Deleted. 31 (globalPrivate.fulfillWithoutPromise): Deleted. 32 (globalPrivate.createResolvingFunctionsWithoutPromise): Deleted. 33 (globalPrivate.promiseReactionJob): Deleted. 34 (globalPrivate.promiseResolveThenableJobFast): Deleted. 35 (globalPrivate.promiseResolveThenableJobWithoutPromiseFast): Deleted. 36 (globalPrivate.promiseResolveThenableJob): Deleted. 37 (globalPrivate.promiseResolveThenableJobWithDerivedPromise): Deleted. 38 (onFulfilled): Deleted. 39 (onRejected): Deleted. 40 (globalPrivate.performPromiseThen): Deleted. 41 * runtime/JSGlobalObject.cpp: 42 (JSC::JSC_DEFINE_HOST_FUNCTION): 43 * runtime/JSMicrotask.cpp: 44 (JSC::createJSMicrotask): 45 * runtime/JSMicrotask.h: 46 1 47 2021-03-01 Yusuke Suzuki <ysuzuki@apple.com> 2 48 -
trunk/Source/JavaScriptCore/builtins/BuiltinNames.h
r273107 r273718 178 178 macro(hasOwnPropertyFunction) \ 179 179 macro(createPrivateSymbol) \ 180 macro(entries) 180 macro(entries) \ 181 macro(outOfLineReactionCounts) 181 182 182 183 -
trunk/Source/JavaScriptCore/builtins/PromiseOperations.js
r273605 r273718 28 28 29 29 @globalPrivate 30 function newPromiseReaction(promiseOrCapability, onFulfilled, onRejected) 31 { 32 "use strict"; 33 34 return { 35 @promiseOrCapability: promiseOrCapability, 36 @onFulfilled: onFulfilled, 37 @onRejected: onRejected, 38 @next: @undefined, 39 }; 30 function pushNewPromiseReaction(thenable, existingReactions, promiseOrCapability, onFulfilled, onRejected) 31 { 32 "use strict"; 33 34 if (!existingReactions) { 35 existingReactions = { 36 @promiseOrCapability: promiseOrCapability, 37 @onFulfilled: onFulfilled, 38 @onRejected: onRejected, 39 // This is 3x then number of out of line reactions (promise, fulfill callback, reject callback). 40 @outOfLineReactionCounts: 0, 41 }; 42 @putPromiseInternalField(thenable, @promiseFieldReactionsOrResult, existingReactions); 43 } else { 44 var outOfLineReactionCounts = existingReactions.@outOfLineReactionCounts; 45 @putByValDirect(existingReactions, outOfLineReactionCounts++, promiseOrCapability); 46 @putByValDirect(existingReactions, outOfLineReactionCounts++, onFulfilled); 47 @putByValDirect(existingReactions, outOfLineReactionCounts++, onRejected); 48 existingReactions.@outOfLineReactionCounts = outOfLineReactionCounts; 49 } 40 50 } 41 51 … … 138 148 "use strict"; 139 149 140 // Reverse the order of singly-linked-list. 141 var previous = @undefined; 142 var current = reactions; 143 while (current) { 144 var next = current.@next; 145 current.@next = previous; 146 previous = current; 147 current = next; 148 } 149 reactions = previous; 150 151 current = reactions; 152 while (current) { 153 @enqueueJob(@promiseReactionJob, state, current, argument); 154 current = current.@next; 155 } 150 if (!reactions) 151 return; 152 153 var isResolved = state === @promiseStateFulfilled; 154 155 @enqueueJob(@promiseReactionJob, state, reactions.@promiseOrCapability, isResolved ? reactions.@onFulfilled : reactions.@onRejected, argument); 156 157 for (var i = 0, count = reactions.@outOfLineReactionCounts; i < count; i += 3) { 158 var promise = reactions[i]; 159 var handler = isResolved ? reactions[i + 1] : reactions[i + 2]; 160 @enqueueJob(@promiseReactionJob, state, promise, handler, argument); 161 } 162 @assert(i === count); 156 163 } 157 164 … … 373 380 374 381 @globalPrivate 375 function promiseReactionJob(state, reaction, argument)382 function promiseReactionJob(state, promiseOrCapability, handler, argument) 376 383 { 377 384 // Promise Reaction has four types. … … 386 393 "use strict"; 387 394 388 var promiseOrCapability = reaction.@promiseOrCapability;389 390 395 // Case (3). 391 if (@isUndefinedOrNull(reaction.@onRejected)) { 392 @assert(@isUndefinedOrNull(reaction.@onFulfilled)); 396 if (@isUndefinedOrNull(handler)) { 393 397 try { 394 398 @assert(@isPromise(promiseOrCapability)); … … 402 406 return; 403 407 } 404 405 var handler = (state === @promiseStateFulfilled) ? reaction.@onFulfilled: reaction.@onRejected;406 408 407 409 // Case (4). … … 449 451 var flags = @getPromiseInternalField(thenable, @promiseFieldFlags); 450 452 var state = flags & @promiseStateMask; 451 var reaction = @newPromiseReaction(promiseToResolve, @undefined, @undefined); 452 if (state === @promiseStatePending) { 453 reaction.@next = @getPromiseInternalField(thenable, @promiseFieldReactionsOrResult); 454 @putPromiseInternalField(thenable, @promiseFieldReactionsOrResult, reaction); 455 } else { 453 var reactionsOrResult = @getPromiseInternalField(thenable, @promiseFieldReactionsOrResult); 454 if (state === @promiseStatePending) 455 @pushNewPromiseReaction(thenable, reactionsOrResult, promiseToResolve, @undefined, @undefined); 456 else { 456 457 if (state === @promiseStateRejected && !(flags & @promiseFlagsIsHandled)) 457 458 @hostPromiseRejectionTracker(thenable, @promiseRejectionHandle); 458 @enqueueJob(@promiseReactionJob, state, reaction, @getPromiseInternalField(thenable, @promiseFieldReactionsOrResult));459 @enqueueJob(@promiseReactionJob, state, promiseToResolve, @undefined, reactionsOrResult); 459 460 } 460 461 @putPromiseInternalField(thenable, @promiseFieldFlags, @getPromiseInternalField(thenable, @promiseFieldFlags) | @promiseFlagsIsHandled); … … 478 479 var flags = @getPromiseInternalField(thenable, @promiseFieldFlags); 479 480 var state = flags & @promiseStateMask; 480 if (state === @promiseStatePending) { 481 var reaction = @newPromiseReaction(@undefined, onFulfilled, onRejected); 482 reaction.@next = @getPromiseInternalField(thenable, @promiseFieldReactionsOrResult); 483 @putPromiseInternalField(thenable, @promiseFieldReactionsOrResult, reaction); 484 } else { 485 var result = @getPromiseInternalField(thenable, @promiseFieldReactionsOrResult); 481 var reactionsOrResult = @getPromiseInternalField(thenable, @promiseFieldReactionsOrResult); 482 if (state === @promiseStatePending) 483 @pushNewPromiseReaction(thenable, reactionsOrResult, @undefined, onFulfilled, onRejected); 484 else { 486 485 if (state === @promiseStateRejected) { 487 486 if (!(flags & @promiseFlagsIsHandled)) 488 487 @hostPromiseRejectionTracker(thenable, @promiseRejectionHandle); 489 @rejectWithoutPromise(re sult, onFulfilled, onRejected);488 @rejectWithoutPromise(reactionsOrResult, onFulfilled, onRejected); 490 489 } else 491 @fulfillWithoutPromise(re sult, onFulfilled, onRejected);490 @fulfillWithoutPromise(reactionsOrResult, onFulfilled, onRejected); 492 491 } 493 492 @putPromiseInternalField(thenable, @promiseFieldFlags, @getPromiseInternalField(thenable, @promiseFieldFlags) | @promiseFlagsIsHandled); … … 547 546 onRejected = @promiseEmptyOnRejected; 548 547 549 var reaction = @newPromiseReaction(promiseOrCapability, onFulfilled, onRejected); 550 548 var reactionsOrResult = @getPromiseInternalField(promise, @promiseFieldReactionsOrResult); 551 549 var flags = @getPromiseInternalField(promise, @promiseFieldFlags); 552 550 var state = flags & @promiseStateMask; 553 if (state === @promiseStatePending) { 554 reaction.@next = @getPromiseInternalField(promise, @promiseFieldReactionsOrResult); 555 @putPromiseInternalField(promise, @promiseFieldReactionsOrResult, reaction); 556 } else { 557 if (state === @promiseStateRejected && !(flags & @promiseFlagsIsHandled)) 558 @hostPromiseRejectionTracker(promise, @promiseRejectionHandle); 559 @enqueueJob(@promiseReactionJob, state, reaction, @getPromiseInternalField(promise, @promiseFieldReactionsOrResult)); 551 if (state === @promiseStatePending) 552 @pushNewPromiseReaction(promise, reactionsOrResult, promiseOrCapability, onFulfilled, onRejected); 553 else { 554 var handler; 555 556 if (state === @promiseStateRejected) { 557 handler = onRejected; 558 if (!(flags & @promiseFlagsIsHandled)) 559 @hostPromiseRejectionTracker(promise, @promiseRejectionHandle); 560 } else 561 handler = onFulfilled; 562 @enqueueJob(@promiseReactionJob, state, promiseOrCapability, handler, reactionsOrResult); 560 563 } 561 564 @putPromiseInternalField(promise, @promiseFieldFlags, @getPromiseInternalField(promise, @promiseFieldFlags) | @promiseFlagsIsHandled); -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
r273138 r273718 482 482 JSValue argument1 = callFrame->argument(2); 483 483 JSValue argument2 = callFrame->argument(3); 484 485 globalObject->queueMicrotask(createJSMicrotask(vm, job, argument0, argument1, argument2)); 486 487 return JSValue::encode(jsUndefined()); 484 JSValue argument3 = callFrame->argument(4); 485 486 globalObject->queueMicrotask(createJSMicrotask(vm, job, argument0, argument1, argument2, argument3)); 487 488 return encodedJSUndefined(); 488 489 } 489 490 -
trunk/Source/JavaScriptCore/runtime/JSMicrotask.cpp
r266074 r273718 38 38 class JSMicrotask final : public Microtask { 39 39 public: 40 static constexpr unsigned maxArguments = 3;41 JSMicrotask(VM& vm, JSValue job, JSValue argument0, JSValue argument1, JSValue argument2 )40 static constexpr unsigned maxArguments = 4; 41 JSMicrotask(VM& vm, JSValue job, JSValue argument0, JSValue argument1, JSValue argument2, JSValue argument3) 42 42 { 43 43 m_job.set(vm, job); … … 45 45 m_arguments[1].set(vm, argument1); 46 46 m_arguments[2].set(vm, argument2); 47 m_arguments[3].set(vm, argument3); 47 48 } 48 49 … … 64 65 } 65 66 66 Ref<Microtask> createJSMicrotask(VM& vm, JSValue job, JSValue argument0, JSValue argument1, JSValue argument2 )67 Ref<Microtask> createJSMicrotask(VM& vm, JSValue job, JSValue argument0, JSValue argument1, JSValue argument2, JSValue argument3) 67 68 { 68 return adoptRef(*new JSMicrotask(vm, job, argument0, argument1, argument2 ));69 return adoptRef(*new JSMicrotask(vm, job, argument0, argument1, argument2, argument3)); 69 70 } 70 71 -
trunk/Source/JavaScriptCore/runtime/JSMicrotask.h
r249509 r273718 35 35 36 36 JS_EXPORT_PRIVATE Ref<Microtask> createJSMicrotask(VM&, JSValue job); 37 JS_EXPORT_PRIVATE Ref<Microtask> createJSMicrotask(VM&, JSValue job, JSValue, JSValue, JSValue );37 JS_EXPORT_PRIVATE Ref<Microtask> createJSMicrotask(VM&, JSValue job, JSValue, JSValue, JSValue, JSValue); 38 38 39 39 } // namespace JSC
Note: See TracChangeset
for help on using the changeset viewer.