[Idea] Specialising based on function parameter values


(Karl) #1

I’ve had this idea floating around my head for a little while, and I’m not sure if it’s either really interesting or totally absurd.
Sorry if it’s not time for ideas like this yet. It’s not really a “proposal”, but it would be ABI-related I think.

So, the idea: The compiler can generate variations of functions where it statically substitutes a type for a placeholder name. Would it also be possible to statically generate a variation of a function if a parameter is a certain value?

I have some code which scans a sequence of bytes. The bytes may be in UTF-8, UTF-16 or UTF-32, and any endianness flavour thereof. Fortunately, the scanner is looking for a ASCII-compatible characters which have the same value (albeit a different size) in each encoding, so this can be implemented as a generic function based on the type of the CodeUnit (UInt8/16/32 respectively). Something roughly like this:

@_specialize(UInt8)
@_specialize(UInt16)
@_specialize(UInt32)
func scanCharacters<U:UnsignedInteger>(from bytes: [UInt8], hasMismatchedEndianness: Bool) -> ... {

  var byteIterator = bytes.makeIterator()
  while let nextByte = byteIterator.next() {

    var codeUnit : U
    // The compiler will statically optimise this branching away because generics.
    if size(of: U.self) == 1 {
      codeUnit = numericCast(nextByte)
    }
    else {
      codeUnit = consumeCodeUnit(withInitialByte: nextByte, source: byteIterator)
      // Even when marking this _slowPath(), there is a significant overhead.
      if hasMismatchedEndianness {
        codeUnit = codeUnit.byteSwapped
      }
    }

    // Process the code-unit
  }
}

However, we still have a problem with this endianness flag. All it requires is that after we consume an entire CodeUnit from the buffer, that we byte-swap it before checking its value. I don’t want to duplicate the entire function code to deal with this one variation in parameter value, but this is a very hot code-path and the branching overhead is significant.

So I would like to tell the compiler that we branch a lot on `hasMismatchedEndianness`, so it can generate and optimise variations of the function while keeping the abstraction level high and the maintenance burden low. There are lots of contexts where this could be useful - not just your typical Boolean switches, but Optionals, too. Even general value-types with specific values that are heavily branched against could benefit from these kinds of optimisations.

Thoughts? Would something like this be possible/valuable?


(Tino) #2

Hi Karl,

I started a discussion about such a concept (afair under the label "compile-time parameters"), and "completing generics" talks about it as well.
Although I think it is quite fundamental (Swift has no arrays of fixed size(!)), and shouldn't be that hard to implement, I'm not sure if it will fit into Swift 4.

- Tino