Regarding the performance of InlineArray: I've seen a recurring framing in this thread that characterizes InlineArray as a risky, niche type best used only in specific situations for targeted performance. Meanwhile, Array is portrayed as the safer, default option, and this framing has led some to suggest withholding syntactic sugar for InlineArray: to "force" developers to confront its supposed danger by writing out the full type name.
I believe this is a mischaracterization. In practice, the performance trade-offs between Array and InlineArray are far more nuanced.
In practice, regular Array is one of the most common sources of performance problems in high-performance Swift. If I take a Swift project off the shelf that is supposed to be fast (and by this, I mean really fast – comparably as fast as a well-written C program would be), and profile it, misuse of Array is by far the most common reason why it isn't.[1] Usually this comes in the form of copying the array, and then later mutating it triggering copy-on-write. This can easily turn linear operations quadratic, and of course if the array contains reference types it will be even more expensive. If you're lucky this problem will be locally evident. What's insidious about this is that the performance symptoms (of triggering a copy of the buffer) often often appear far away from the cause (the copy of the array value), and tracking down that root cause can be really tough, similar to finding the source of a memory corruption bug.
Effectively CoW defeats local reasoning about performance. Unpicking this can often be quite painful.[2] For example you might need to start swapping arrays with empty ones, in order to move the buffer between two locations – but this means tracking down all the places in your code that are assuming "these values will be here at this point". If you don't have good unit test coverage, this can be really hard.
On top of this, Array has a number of lesser performance challenges:
Array's CoW feature means mutation often requires an additional check for uniquness, which is a tax like reference counting.- It's easy to forget to reserve space up front, causing multiple reallocations. Yes exponential growth means these are amortized, but they still cost a lot in a hot loop.
- The dynamic size means the compiler does not always emit optimal code when looping over the array, even if in theory the size is statically known at compile time.
- Two dimensional arrays are syntactically appealing i.e.
[[Int]]but almost never a good idea.
By contrast, InlineArray's one specific performance challenge – that it costs to make a copy – is at least something you can reason about locally. The perf hit will show up in the trace exactly where it is caused, and so is easily resolved. To me, the naming of InlineArray is not "a warning". It's just the best name to describe what this type is: it's an array type that holds all its elements inline. The fixed size nature of it falls out of that property.
The name Array is the anomoly here, because it makes no effort to be more descriptive of its characteristics. I expect if we had a naming thread about it today, there would be a large constituency for calling it COWArray or DynamicArray or even GrowableCopyOnWriteArray.[3] However, I do not believe such naming would help fix the performance troubles folks hit while using it, nor should it mean Array shouldn't have sugar. And the same goes for InlineArray.
This is not a "this thing is bad, therefore we should allow this other thing to be bad" argument. It is more that there is far more to writing high performance code than just being prompted by some naming. If you want your code to be fast, you really need to understand the characteristics of the types you are using, and the trade-offs they bring. I do not think sugaring the syntax of this type changes that calculus either way.
Misuse of classes is the second most common. In fact, these two frequently combine together and compound each other. ↩︎
Much like if you've baked reference semantics into your code by using classes, only to find the classes should have been structs for performance. ↩︎
We will experience this fun soon, since I believe it's important to add a noncopyable growable array type - one that prevents accidental sharing and does not require refcounting or checks for uniqueness. ↩︎