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.headerwith 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
ManagedBuffershould prefer to usewithUnsafeMutablePointerToHeaderrather than theheaderproperty, in the same way we advise them to store the capacity in the header rather than using thecapacityproperty.
Thoughts?