I've noticed a bit of a performance footgun with ManagedBuffer.
The header
is written as a class property in the standard library's source code. This means it requires dynamic enforcement of the law of exclusivity, which can have significant performance consequences.
For example, typically users of ManagedBuffer
will store the buffer's count
and capacity
within the header, and will want to ensure that accesses to the elements are bounds-checked. This means every access to an element must also access the header, resulting in runtime calls for exclusivity enforcement. In some loops, I've seen that enforcement account for up to 75% of the code's runtime.
But this enforcement is generally not necessary: ManagedBuffer
is typically used to build
copy-on-write data structures with value semantics, so exclusivity is enforced at a higher level than the
compiler is able to reason about. Indeed, it is not even possible to access a MB's elements except through an unsafe pointer (which has no exclusivity enforcement), so developers already need to manually ensure that overlapping writes do not occur.
So I'm wondering what we should do about this. I have 2 ideas:
-
I notice that @Erik_Eckstein recently added an attribute to disable exclusivity enforcement on a per-property basis. We could perhaps annotate
ManagedBuffer.header
with this attribute. I'm not sure if that would technically count as a behaviour change -- it is a change in enforcement of a rule that developers should have been obeying anyway, and have to obey in order to safely use theManagedBuffer
's elements. -
Alternatively, we should document that anybody implementing a COW type using
ManagedBuffer
should prefer to usewithUnsafeMutablePointerToHeader
rather than theheader
property, in the same way we advise them to store the capacity in the header rather than using thecapacity
property.
Thoughts?