This question is somewhere between swift-dev and swift-users, not sure where best to post this.
I’m working on a project that wants to get very low-abstraction penalty array operations, particularly with varargs.
It’s awesome to hear you’re still using Swift
Indeed, it is nice to get to use it as well as hack on it, instead of largely just hacking on it :-). OTOH, this makes me as impatient as everyone else to get conditional conformances, recursive conformances, varargs splat, and so many other things.
Is there a better way to achieve this, and if not, is there any planned work (perhaps aligned with the ABI stability efforts) to improve vararg array performance to be able to avoid heap abstractions? Any individual call to a vararg array is a known length, so it is a perfect candidate for a stack allocation.
In fact I filed an internal radar about this a while ago, but the discussion didn’t go anywhere because we had bigger fish to fry.
I think I confused things in my email. The long term points that you mention are important (and I’ll comment on them below), but independently of the representation of varargs, there is a generally useful optimization that this code should be able to benefit from: when converting a statically knowable fixed sized array to an UnsafeBufferPointer (or UnsafePointer) for a call, we can get rid of the heap traffic in the client. This is generally useful e.g. when calling into random C APIs.
I’m curious to know if anyone has thought about that or plans to work on it, because it would help for a lot of kinds of code, and is related to the existing “outlining” optimization. Coming back to varargs though:
I think the main difficulty is that we would need some kind of “copy-on-escape” sequence type. Iterating over the vararg inside the callee should be efficient, but if I then take the value and store it somewhere else, it would need to copy the buffer from the stack to the heap.
Also there were source stability implications because people assume the vararg argument inside the function body is literally a Swift.Array.
Perhaps the work being done on ownership can help here though. In the ownership manifesto, John describes a ‘shared’ attribute on function parameters.
Right. If we ignore source compatibility for the moment, it seems clear that the best callee side representation for varargs is a borrowing array slice (like llvm::ArrayRef or UnsafeBufferPointer). This provides the essential abstraction needed by varargs without forcing an onerous representation on the caller, and without implying any reference counting operations. This would allow passing an Array, an ArraySlice, a StaticArray, or anything else with contiguous elements.
What if I declare my vararg as shared:
func takesArgs(_ xs: shared Int…)
This seems like a great way to address the source compatibility issue: a normal varargs argument could be passed as an Array<T> for compatibility with existing source, and a “borrowed Int…” could be passed more efficiently, allowing folks to opt into that.
This means the caller is passing along a borrowed value, not the value itself. With today’s vararg implementation this would just pass the Array<Int> at +0, but perhaps this can be the extension point where we introduce the stack-allocated varargs that you propose, and say that a ‘shared’ vararg argument actually has a more efficient representation. This allows it to be introduced additively without breaking ABI or source compatibility.
What do you think?
This would be totally awesome. How much do you think that people depend on varargs being an escapable array inside the body of functions? It would be much cleaner to just make them nonescaping slices all the time.
On Oct 6, 2017, at 11:12 PM, Slava Pestov <firstname.lastname@example.org> wrote:
On Oct 6, 2017, at 11:06 PM, Chris Lattner via swift-dev <email@example.com> wrote: