Changeset 295036 in webkit for trunk/Source/JavaScriptCore/heap


Ignore:
Timestamp:
May 30, 2022, 4:41:10 PM (3 years ago)
Author:
[email protected]
Message:

[JSC] Make Strong::set cheap
https://bugs.webkit.org/show_bug.cgi?id=241090

Reviewed by Mark Lam.

HandleSet::writeBarrier is frequently called because it is called every time we set a value in Strong<>.
This patch optimizes it,

  1. We should make it inline function since it has a super fast path major use can be covered. And this function is small.
  2. We should not always remove a node from the list first. We should insert / remove it only when necessary.
  3. Remove m_immediateList since it is not necessary.
  4. Make HandleNode as a derived class of BasicRawSentinelNode to make implementation simpler.

This change improves promise benchmarks score since promise uses microtasks which hold values via Strong<>.

ToT
Time(doxbee-async-bluebird): 42.8 ms.
Time(doxbee-async-es2017-babel): 36.4 ms.
Time(doxbee-async-es2017-native): 28.3 ms.
Time(doxbee-promises-bluebird): 514.2 ms.
Time(doxbee-promises-es2015-native): 44.8 ms.
Time(fibonacci-async-es2017-babel): 380.5 ms.
Time(fibonacci-async-es2017-native): 218.2 ms.
Time(parallel-async-bluebird): 648.8 ms.
Time(parallel-async-es2017-babel): 116.9 ms.
Time(parallel-async-es2017-native): 115.6 ms.
Time(parallel-promises-bluebird): 638 ms.
Time(parallel-promises-es2015-native): 82 ms.

Patched
Time(doxbee-async-bluebird): 38 ms.
Time(doxbee-async-es2017-babel): 27 ms.
Time(doxbee-async-es2017-native): 19.5 ms.
Time(doxbee-promises-bluebird): 508.3 ms.
Time(doxbee-promises-es2015-native): 33.3 ms.
Time(fibonacci-async-es2017-babel): 349.1 ms.
Time(fibonacci-async-es2017-native): 151 ms.
Time(parallel-async-bluebird): 639.6 ms.
Time(parallel-async-es2017-babel): 100.9 ms.
Time(parallel-async-es2017-native): 101.9 ms.
Time(parallel-promises-bluebird): 614 ms.
Time(parallel-promises-es2015-native): 70.9 ms.

  • Source/JavaScriptCore/heap/HandleSet.cpp:

(JSC::HandleSet::writeBarrier): Deleted.

  • Source/JavaScriptCore/heap/HandleSet.h:

(JSC::HandleSet::heapFor):
(JSC::HandleSet::allocate):
(JSC::HandleSet::deallocate):
(JSC::HandleSet::writeBarrier):
(JSC::HandleSet::toHandle): Deleted.
(JSC::HandleSet::toNode): Deleted.
(JSC::HandleNode::HandleNode): Deleted.
(JSC::HandleNode::setPrev): Deleted.
(JSC::HandleNode::prev): Deleted.
(JSC::HandleNode::setNext): Deleted.
(JSC::HandleNode::next): Deleted.

  • Source/JavaScriptCore/heap/Strong.h:

(JSC::Strong::set):

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

Location:
trunk/Source/JavaScriptCore/heap
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/heap/HandleSet.cpp

    r290705 r295036  
    7171template void HandleSet::visitStrongHandles(SlotVisitor&);
    7272
    73 void HandleSet::writeBarrier(HandleSlot slot, const JSValue& value)
    74 {
    75     if (!value == !*slot && slot->isCell() == value.isCell())
    76         return;
    77 
    78     Node* node = toNode(slot);
    79 #if ENABLE(GC_VALIDATION)
    80     RELEASE_ASSERT(isLiveNode(node));
    81 #endif
    82     SentinelLinkedList<Node>::remove(node);
    83     if (!value || !value.isCell()) {
    84         m_immediateList.push(node);
    85         return;
    86     }
    87 
    88     m_strongList.push(node);
    89 #if ENABLE(GC_VALIDATION)
    90     RELEASE_ASSERT(isLiveNode(node));
    91 #endif
    92 }
    93 
    9473unsigned HandleSet::protectedGlobalObjectCount()
    9574{
  • trunk/Source/JavaScriptCore/heap/HandleSet.h

    r290705 r295036  
    4040class JSValue;
    4141
    42 class HandleNode {
     42class HandleNode final : public BasicRawSentinelNode<HandleNode> {
    4343public:
    44     HandleNode(WTF::SentinelTag);
    45     HandleNode();
     44    HandleNode() = default;
    4645   
    4746    HandleSlot slot();
    4847    HandleSet* handleSet();
    4948
    50     void setPrev(HandleNode*);
    51     HandleNode* prev();
    52 
    53     void setNext(HandleNode*);
    54     HandleNode* next();
     49    static HandleNode* toHandleNode(HandleSlot slot)
     50    {
     51        return bitwise_cast<HandleNode*>(bitwise_cast<uintptr_t>(slot) - OBJECT_OFFSETOF(HandleNode, m_value));
     52    }
    5553
    5654private:
    57     JSValue m_value;
    58     HandleNode* m_prev;
    59     HandleNode* m_next;
     55    JSValue m_value { };
    6056};
    6157
     
    7571    template<typename Visitor> void visitStrongHandles(Visitor&);
    7672
    77     JS_EXPORT_PRIVATE void writeBarrier(HandleSlot, const JSValue&);
     73    template<bool isCellOnly>
     74    void writeBarrier(HandleSlot, JSValue);
    7875
    7976    unsigned protectedGlobalObjectCount();
     
    8380private:
    8481    typedef HandleNode Node;
    85     static HandleSlot toHandle(Node*);
    86     static Node* toNode(HandleSlot);
    8782
    8883    JS_EXPORT_PRIVATE void grow();
    8984   
    9085#if ENABLE(GC_VALIDATION) || ASSERT_ENABLED
    91     bool isLiveNode(Node*);
     86    JS_EXPORT_PRIVATE bool isLiveNode(Node*);
    9287#endif
    9388
     
    9590    DoublyLinkedList<HandleBlock> m_blockList;
    9691
    97     SentinelLinkedList<Node> m_strongList;
    98     SentinelLinkedList<Node> m_immediateList;
     92    using NodeList = SentinelLinkedList<Node, BasicRawSentinelNode<Node>>;
     93    NodeList m_strongList;
    9994    SinglyLinkedList<Node> m_freeList;
    10095};
     
    10297inline HandleSet* HandleSet::heapFor(HandleSlot handle)
    10398{
    104     return toNode(handle)->handleSet();
     99    return HandleNode::toHandleNode(handle)->handleSet();
    105100}
    106101
     
    108103{
    109104    return m_vm;
    110 }
    111 
    112 inline HandleSlot HandleSet::toHandle(HandleSet::Node* node)
    113 {
    114     return reinterpret_cast<HandleSlot>(node);
    115 }
    116 
    117 inline HandleSet::Node* HandleSet::toNode(HandleSlot handle)
    118 {
    119     return reinterpret_cast<HandleSet::Node*>(handle);
    120105}
    121106
     
    127112    HandleSet::Node* node = m_freeList.pop();
    128113    new (NotNull, node) HandleSet::Node();
    129     m_immediateList.push(node);
    130     return toHandle(node);
     114    return node->slot();
    131115}
    132116
    133117inline void HandleSet::deallocate(HandleSlot handle)
    134118{
    135     HandleSet::Node* node = toNode(handle);
    136     SentinelLinkedList<HandleSet::Node>::remove(node);
     119    HandleSet::Node* node = HandleNode::toHandleNode(handle);
     120    if (node->isOnList())
     121        NodeList::remove(node);
    137122    m_freeList.push(node);
    138 }
    139 
    140 inline HandleNode::HandleNode()
    141     : m_prev(nullptr)
    142     , m_next(nullptr)
    143 {
    144 }
    145 
    146 inline HandleNode::HandleNode(WTF::SentinelTag)
    147     : m_prev(nullptr)
    148     , m_next(nullptr)
    149 {
    150123}
    151124
     
    158131{
    159132    return HandleBlock::blockFor(this)->handleSet();
    160 }
    161 
    162 inline void HandleNode::setPrev(HandleNode* prev)
    163 {
    164     m_prev = prev;
    165 }
    166 
    167 inline HandleNode* HandleNode::prev()
    168 {
    169     return m_prev;
    170 }
    171 
    172 inline void HandleNode::setNext(HandleNode* next)
    173 {
    174     m_next = next;
    175 }
    176 
    177 inline HandleNode* HandleNode::next()
    178 {
    179     return m_next;
    180133}
    181134
     
    192145}
    193146
     147template<bool isCellOnly>
     148inline void HandleSet::writeBarrier(HandleSlot slot, JSValue value)
     149{
     150    bool valueIsNonEmptyCell = value && (isCellOnly || value.isCell());
     151    bool slotIsNonEmptyCell = *slot && (isCellOnly || slot->isCell());
     152    if (valueIsNonEmptyCell == slotIsNonEmptyCell)
     153        return;
     154
     155    Node* node = HandleNode::toHandleNode(slot);
     156#if ENABLE(GC_VALIDATION)
     157    if (node->isOnList())
     158        RELEASE_ASSERT(isLiveNode(node));
     159#endif
     160    if (!valueIsNonEmptyCell) {
     161        ASSERT(slotIsNonEmptyCell);
     162        ASSERT(node->isOnList());
     163        NodeList::remove(node);
     164        return;
     165    }
     166
     167    ASSERT(!slotIsNonEmptyCell);
     168    ASSERT(!node->isOnList());
     169    m_strongList.push(node);
     170
     171#if ENABLE(GC_VALIDATION)
     172    RELEASE_ASSERT(isLiveNode(node));
     173#endif
     174}
     175
    194176} // namespace JSC
  • trunk/Source/JavaScriptCore/heap/Strong.h

    r283851 r295036  
    142142        ASSERT(slot());
    143143        JSValue value = HandleTypes<T>::toJSValue(externalType);
    144         HandleSet::heapFor(slot())->writeBarrier(slot(), value);
     144        HandleSet::heapFor(slot())->template writeBarrier<std::is_base_of_v<JSCell, T>>(slot(), value);
    145145        *slot() = value;
    146146    }
Note: See TracChangeset for help on using the changeset viewer.