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

Add ephemeron table support #636

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

XmiliaH
Copy link
Contributor

@XmiliaH XmiliaH commented Aug 11, 2022

This PR shows a possible way to implement ephemeron tables in an efficient manner. While the implementation in current versions of Lua uses a fix point search which revisits ephemeron tables in case some object were marked during the last iteration resulting in a quadratic runtime behaviour in the worst case. However, this implementation uses a different algorithmus ensuring a linear runtime behaviour.

Implementation Details

This implementation has a notion of ephemeron keys. These are white keys in an ephemeron table. When these are found during visit of an ephemeron table in the atomic phase the table node will be linked to the ephemeron key. This allows when the key is marked to visit all the nodes and mark the values.

To understand the implementation in more detail one needs to look at the LuaNode table node structure. The following is a representation of the struct.

+----------------+--------+--------+----------------+--------+-+-------+
| Value union    | VExtra | ValTT  | Key union      | KExtra | | Link  |
+----------------+--------+--------+----------------+--------+-+-------+
                                                              ^
                                                            Key TT

To add such nodes to a list, only slots which can be restored can be used.
The value slots except for the type tag slot fall away, as there is not way to restore them. However, the tag slot can be used as the 4 bit type tag can be saved and then the 32 bit slot can be used for other data. The key union can be restored, as the node will be in a list starting from the object which the key pointer pointed to. Furthermore, the key extra slot is not used as we do not have a vector type, so these 32 bit can be used. The key type tag can be used for other values as it can be restored by the key object type tag.

In this implementation the node can be in one of two lists. The ephemeron list which starts at the ephemeron key and the worklist which is used during marking of the ephemeron key to avoid recursion.

When the node is in the ephemeron list the key union points to the next node instead of the object and the object points to the first node. In the userdata case the pointer in the object is stored in the metatable pointer and the last element in the list points to the metatable allowing to restore the value. In all other cases the gclink pointer is used as it is unused when the object is white. The last value in the list can be detected by a bit set in the extra slot of the key. Furthermore, the value type tag is saved in the extra slot of the key and the value type tag is set to nil and the key type tag to a dead key. This allows to skip cleanup for dead keys as these entries look like dead nodes in this case.

When during marking an ephemeron key is encountered the ephemeron list is added to the working list. While relinking the key union is restored, the value type tag is moved from the extra key slot to the key type tag slot and the extra key slot and value type tag slots are used to store the pointer to the next entry in the worklist.

When working throug the worklist to mark all the values the node is finally restored by moving the value type tag from the key type tag back and restoring the key type tag from the key value.

Remarks

  • This implementation violates some assumptions of the layout of taged values during the atomic phase. Since during the atomic pahse the nodes will not be revisited and the mutator is not running this should not result in actual problems.
  • This adds some assumtions which might change in the future:
    • Type tag is 4 bit
    • Value has a type tag slot of 32 bit
    • Key has a extra slot of 32 bit

@zeux
Copy link
Collaborator

zeux commented Aug 15, 2022

Thanks - this is an interesting concept! Luau currently intentionally doesn't support ephemerons, but at some point in the future we will likely reevaluate that and knowing that there's a different implementation strategy helps.

Some of the extra restrictions that would be imposed by this implementation are uncomfortable long term (eg we've looked into changing the type tag slot to 8 bit for some speculative future types as well as using extra for some GC types, although the latter may be less of a problem if you can recover it from the GC value).

There are also considerations wrt scalability limits imposed by weak table scanning - right now applications with a large amount of weak tables have significant / unbounded GC pauses during atomic stage; the current implementation can be adapted to speculatively scan the tables earlier to reduce that, but with ephemerons this may or may not become impossible, with the original implementation technique or this one, so this would need to be studied carefully.

@zeux
Copy link
Collaborator

zeux commented Aug 15, 2022

I'll convert this to draft since we currently don't intend to commit to implementing ephemerons, but I don't want to lose this change for further evaluation.

@zeux zeux marked this pull request as draft August 15, 2022 17:02
@XmiliaH
Copy link
Contributor Author

XmiliaH commented Aug 15, 2022

First of all, thanks for looking into this PR.

knowing that there's a different implementation strategy helps

In that case this PR did it's job.

eg we've looked into changing the type tag slot to 8 bit for some speculative future types

Can I ask what the plan would be in that case for the 4 bit key type tag? Take 4 more bits from the link for the type tag?

the current implementation can be adapted to speculatively scan the tables earlier to reduce that, but with ephemerons this may or may not become impossible

I would not see how that should become impossible. The ephemeron tables can be handled like normal weak tables before the atomic phase. Only in the atomic phase they become special. So one could revisit the ephemeron table before the atomic phase multiple times. In this PR when the table is found in the GCSpropagate phase it is actually revisited in the GCSpropagateagain phase since it is put into the grayagain list.

@zeux
Copy link
Collaborator

zeux commented Aug 15, 2022

Can I ask what the plan would be in that case for the 4 bit key type tag? Take 4 more bits from the link for the type tag?

Sorry, I wasn't clear here: we were not considering much larger type tags - although right now we actually have 5 bits worth of space in LuaNode (the next field is 28 bits, but the largest table is 2^26 entries, so next must be in range of [-(2^26-1), 2^26-1], for which 27 bits is enough) - we weren't sure about whether to expand the table to 2^27 entries max or expand tt to 5 bits, but it's likely that we would do the latter instead, since we are pretty close to 16 types). What we considered instead is effectively increasing extra storage from 4 bytes to 7 bytes for some value types, and shrinking Value::tt to 1 byte, which would reduce the amount of space you can steal from LuaNode value storage.

@XmiliaH
Copy link
Contributor Author

XmiliaH commented Aug 15, 2022

What we considered instead is effectively increasing extra storage from 4 bytes to 7 bytes for some value types, and shrinking Value::tt to 1 byte

But would that not also increase the extra slot of the key and then there is no space for the link slot.

@zeux
Copy link
Collaborator

zeux commented Aug 15, 2022

Ah yeah you're right - this is part of why we never went there :) My larger point was that I'd want to be very careful about building a user-facing feature that places extra constraints on easily available space in the nodes, but it would indeed be valuable to look at the actual changes we might want to do and evaluate them individually against the question of having extra temporary space available in node storage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants