dotvvm icon indicating copy to clipboard operation
dotvvm copied to clipboard

Identify DotvvmProperty with systematic 32-bit IDs

Open exyi opened this issue 1 year ago • 1 comments

All DotvvmProperties now get assigned 32-bit IDs, which can be used for more efficient lookup and identification. The has a ID 16b + 16b structure to allow optimizing certain common operations and make the assignment consistent even when we initialize controls on multiple threads.

The ID format is (bit 0 is (id >> 31)&1, bit 31 is id&1)

  • bit 0
    • =1 - is property group
      • bits 16-30: Identifies the property declaring type.
      • bits 0-15: Identifies the property in the declaring type.
    • =0 - any other DotvvmProperty
      • bits 16-30: Identifies the property group
      • bits 0-15: Identifies a string - the property group member

All IDs are assigned sequentially, with reserved blocks at the start for the most important types which we might want to address directly in a compile-time constant.

IDs put a limit on the number of properties in a type (>= 32k), the number of property groups (32k), and the number of property group members (64k). All property groups share the same name dictionary, which allows for significant memory savings, but it might be limiting in obscure cases.

As property groups share the name-ID mapping, we do not to keep the GroupedDotvvmProperty instances in memory after the compilation is done. VirtualPropertyGroupDictionary will map strings directly to the IDs and back, without going to GroupedDotvvmProperty.

Shorter unmanaged IDs allows for efficient lookup in unorganized arrays using SIMD – 8 property IDs fit into a single vector register. Since controls with more than 8 properties are not common, the brute force will actually be faster than anything. The custom perfect-hashing algorithm is therefore removed. Possible states of the DotvvmControlProperties dictionary are:

  • Empty: keys and values are both null (this is default struct state)
  • Array8: keys and values are 8 element arrays without any ordering guarantees (keys is 256 bits)
  • Array16: keys and values are 16 element arrays without any ordering guarantees (keys is 512 bits)
  • Dictionary: keys is null, values is Dictionary<DotvvmPropertyId, object?>. This is mainly fallback for the rare case someone puts this many properties onto a single control.

Moreover, the dictionary remembers two booleans: ownsValues and ownsKeys which are used for copy-on-write control cloning.

  • When new arrays are initialized, both are set to true
  • When control is cloned, both are set to false, and the dictionary is copied by reference.
    • Exception being if any property contains an instance of DotvvmBindableObject
      • the object must be cloned recursively, and we therefore clone the values array, while still copying keys by reference
      • result is: ownsValues = true, ownsKeys = false
  • When existing property is updated:
    • If !ownsValues, we clone the values array and set ownsValues = true (ownsKeys is untouched)
  • When new property is added / When property is removed:
    • If !ownsKeys, we clone the keys array and set ownsKeys = true
    • If !ownsValues, we clone the values array and set ownsValues = true

Number of other places in the framework were adjusted to address properties directly using the IDs to take advantage of the performance gains.

exyi avatar Oct 04 '24 11:10 exyi