Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JSC] Make Strong::set cheap #1150

Conversation

Constellation
Copy link
Member

@Constellation Constellation commented May 30, 2022

6b54cb4

[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
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295036 268f45cc-cd09-0410-ab3c-d52691b4dbfc

@Constellation Constellation self-assigned this May 30, 2022
@Constellation Constellation added JavaScriptCore For bugs in JavaScriptCore, the JS engine used by WebKit, other than kxmlcore issues. WebKit Nightly Build labels May 30, 2022
Copy link

@MenloDorian MenloDorian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=me. I think there's at least one case in HandleSet::writeBarrier below where the isOnList check can be turned into an ASSERT. And if we can strengthen the entry check in HandleSet::writeBarrier, we may be able to remove the other isOnList check too.

Comment on lines 150 to 157
if (!value == !*slot) {
if constexpr (isCellOnly) {
ASSERT(slot->isCell() == value.isCell());
return;
}
if (slot->isCell() == value.isCell())
return;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see ... the possibilities are:

  1. value == empty, *slot == empty: then slot->isCell() is true and value.isCell() is true even though empty != cell. But this is ok because we don't need to so anything if both are empty.

  2. value == cell, *slot == cell: this is ok too because we don't need to do anything.

  3. value == number, *slot == number: this is ok too because we don't need to do anything.

  4. value == cell, *slot == number: in the generic case, the (slot->isCell() == value.isCell()) test will fail, and we won't return early, which is good. In the isCellOnly case, this is impossible.

  5. value == cell, *slot == empty: the (!value == !*slot) test will fail, and we won't return early, which is good.

  6. value == empty, *slot == cell: the (!value == !*slot) test will fail, and we won't return early, which is good.

  7. value == empty, *slot == number: the (!value == !*slot) test will fail, and we won't return early, which is good. However, we'll need to check below if the node is already on list before removing. This case is impossible for the isCellOnly case.

  8. value == number, *slot == empty: the (!value == !*slot) test will fail, and we won't return early, which is not great. And we'll need to check below if value is a cell before adding it to m_strongList. This case is impossible for the isCellOnly case.

  9. value == number, *slot == cell: in the generic case, the (slot->isCell() == value.isCell()) test will fail, and we won't return early, which is good. However, we'll need to check below if value is a cell before adding it to m_strongList. This case is impossible for the isCellOnly case.

I'm using "number" to represent all the other types of JSValue that are non-cell but are not empty as well.

Comment on lines 164 to 167
if (!value) {
ASSERT(node->isOnList());
NodeList::remove(node);
return;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we get here, if value is empty, then we know that *slot must have a cell. Hence, we should remove the node from the m_strongList. So, this is correct.

Comment on lines 169 to 170
ASSERT(!node->isOnList());
m_strongList.push(node);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we get here, then we know that *slot must not have had a cell (because value has a cell). So, we must push the node onto m_strongList. So, this is also correct.

Comment on lines 178 to 179
if (!node->isOnList())
m_strongList.push(node);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we get here, value must be a cell. But we know from the entry check that the only way that we can get here is if *slot is either empty or a number. In both case, node should not already be on m_strongList. So, we can turn this if check into an ASSERT.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, fixed.

Comment on lines 173 to 174
if (node->isOnList())
NodeList::remove(node);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we get here, then we know value is not cell. Because of the entry check, we know that *slot can either be a number of a cell. So, it makes sense that we need this isOnList check. However, if the entry check also ensures that *slot cannot be a number, then we can turn this isOnList check into an ASSERT.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I've changed the condition,

  150     bool valueIsNonEmptyCell = value && (isCellOnly || value.isCell());
  151     bool slotIsNonEmptyCell = *slot && (isCellOnly || slot->isCell());
  152     if (valueIsNonEmptyCell == slotIsNonEmptyCell)
  153         return;

So, now, after this fast path check, we can make this isOnList() removed :)

@webkit-early-warning-system webkit-early-warning-system added the merging-blocked Applied to prevent a change from being merged label May 30, 2022
@Constellation Constellation removed merging-blocked Applied to prevent a change from being merged JavaScriptCore For bugs in JavaScriptCore, the JS engine used by WebKit, other than kxmlcore issues. WebKit Nightly Build labels May 30, 2022
@Constellation Constellation added JavaScriptCore For bugs in JavaScriptCore, the JS engine used by WebKit, other than kxmlcore issues. WebKit Nightly Build labels May 30, 2022
@webkit-early-warning-system webkit-early-warning-system added the merging-blocked Applied to prevent a change from being merged label May 30, 2022
@Constellation Constellation added merge-queue Applied to send a pull request to merge-queue and removed merging-blocked Applied to prevent a change from being merged labels May 30, 2022
@webkit-early-warning-system webkit-early-warning-system merged commit 6b54cb4 into WebKit:main May 30, 2022
@webkit-early-warning-system
Copy link
Collaborator

Committed r295036 (251131@main): https://commits.webkit.org/251131@main

Reviewed commits have been landed. Closing PR #1150 and removing active labels.

@webkit-early-warning-system webkit-early-warning-system removed the merge-queue Applied to send a pull request to merge-queue label May 30, 2022
guijemont pushed a commit to guijemont/WebKit that referenced this pull request Aug 30, 2023
…ki/linux_MemoryPressureMonitor_cgroupV1_honors_memory.memsw.usage_in_bytes_if_exist

Linux MemoryPressureMonitor (cgroupV1) honors memory.memsw.usage_in_b…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
JavaScriptCore For bugs in JavaScriptCore, the JS engine used by WebKit, other than kxmlcore issues.
Projects
None yet
3 participants