-
Notifications
You must be signed in to change notification settings - Fork 394
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
base: master
Are you sure you want to change the base?
Conversation
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. |
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. |
First of all, thanks for looking into this PR.
In that case this PR did it's job.
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?
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 |
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 |
But would that not also increase the extra slot of the key and then there is no space for the link slot. |
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. |
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.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