[SR-3340] Evaluate the ABI for existential container storage: size of the inline value buffer

As part of [SR-3340] Evaluate the ABI for existential container storage. · Issue #45928 · apple/swift · GitHub we put the evaluation of the size of the inline buffer on our plate. In the bug I propose to not change it. Data based decision is usually a sensible approach but I claim we don't have the necessary experimental setup (proper benchmarks) to get sensible data a decision can be based on. This might be controversial. My reasoning there:

It is implied in this bug that we have a sensible way of evaluating the decision what size the inline storage should be based on some benchmarks.

I claim we don't have the set of benchmarks available to make a sensible data based decision:

I think for a truly meaning full data base decision we would have to evaluate a representative set of programs against a metric like minimizing heap memory waste vs performance over the dynamic execution of this set of programs. We would need system'y benchmarks (i.e larger applications that model properties of large frameworks or system applications).

I posit that we don't have this set of benchmarks available (our swift/benchmarks certainly don't qualify IMO) nor do we have the time to construct them.

Short of that I think that it is going to be intuition what a good size is based on some sensible rules:

  • One that we expect to miminize memory waste and maximizes performance.
  • This is going to be around the size where we are today (IMO).
  • The size should work well with common standard library types:
  • String is going to be two words
  • StringSlice is going to be three words

This favors the status quo. The status quo is further favored by the fact that existing applications rely on it implicitly.

What would we get out of an experiment running the benchmarks:

  • Assume 2 word buffer regresses unacceptly; then we would argue we have to stay with a 3 word buffer (which is what we are proposing without the experiment).
  • Assume it does not; what would be the conclusion. I would not want to move to a 2 word buffer based on this because i don’t believe that the benchmark to be representative of performance or memory requirements in a system setting.

In the absence of useful empirical data, you should go with analytic data. In Swift 1 we set the existential size to be big enough to hold string and array instances. Slice instances are not important to size to in this case IMO.

Getting it down to a 2 word existential buffer will be a big improvement.

Dumb question: isn't the size of StringSlice relevant because of StringProtocol?

Right, that is what I meant by intuition/sets of rules.

So the argument you make is that we should a:

  • Make it as small as possible
  • Bounded by the max size of all major standard library types: Array, Character, Dictionary, String are at most two words and therefore two is the right answer.
  • We don't expect Slice, StringSlices in existential containers to be an important/common case
  • Optional<String | Dictionary | Array> currently fit into two words and will remain so

CC @Ben_Cohen @Michael_Ilseman

Substring (“StringSlice”) being 3 words is dependent on some further tweaks and tricks, which may or may not pan out. It may be just like all other slices: two extra words. I wouldn’t base evaluation on it being 3 words just yet.

That being said, inline buffer size of 2 makes Any 3 words and single-protocol existential 4 words. Fitting in 4 words total is a kind of “sweet spot” in our calling convention, where even on 32-bit ARM we can fit it in registers for return values. (3-word Any fits i386 well, FWIW).

This suggest another analytical direction that considers the calling convention and whether single-protocol existentisls are common arguments or return types.

edit: Existentials are not passed as values

We don't pass existentials by value. This is unlikely to change because you will have to lower the value to memory for protocol method calls.

1 Like