Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (Cyborg)
  • No Skin
Collapse
Brand Logo

CIRCLE WITH A DOT

  1. Home
  2. Uncategorized
  3. I landed some performance improvements for the Swift type checker recently, and I'm currently finishing up the next set of changes which I hope to merge soon.

I landed some performance improvements for the Swift type checker recently, and I'm currently finishing up the next set of changes which I hope to merge soon.

Scheduled Pinned Locked Moved Uncategorized
17 Posts 7 Posters 26 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • slava@mathstodon.xyzS slava@mathstodon.xyz

    So the constraint solver has an extra step between solving constraints and selecting a disjunction; it tries to attempt a _type variable binding_. It looks at the set of unsolved conversion constraints and tries to "guess" one of them, and binds that type variable.

    The problem here is to basically compute the domain for each type variable--the set of possible concrete types it might have.

    Often, constraint systems have quite simple domains, finite sets for example (and in SAT it's just {0, 1}). In Swift's case, the set of "all possible types" is infinite, because of generics, and quite complicated, so we cannot represent a domain exactly, but we can apply some rough heuristics.

    If we can prove that a type variable's domain consists of exactly one type, we can bind it to that type variable, and if we can prove the domain is empty, we can bind it to anything; no matter what, some constraint will fail.

    slava@mathstodon.xyzS This user is from outside of this forum
    slava@mathstodon.xyzS This user is from outside of this forum
    slava@mathstodon.xyz
    wrote last edited by
    #7

    In binding inference, we had an existing optimization that would consider types that have no proper subtypes. For example, if you have

    $T0 conv Int

    Then in fact we know that Int (like almost all structs, and all user-defined structs) has no proper subtypes, that is, types that convert to it. So we can always bind $T0 to Int in this case.

    I generalized this idea further. Suppose you instead have:

    Int conv $T0

    You can't apply the same optimization here, and just say that $T0 is Int. In Swift, _every_ type has supertypes; those are an optional wrapping the type, the existential types to which this type conforms, and the special standard library AnyHashable type, which is a supertype of every concrete type that conforms to Hashable.

    slava@mathstodon.xyzS 1 Reply Last reply
    0
    • slava@mathstodon.xyzS slava@mathstodon.xyz

      In binding inference, we had an existing optimization that would consider types that have no proper subtypes. For example, if you have

      $T0 conv Int

      Then in fact we know that Int (like almost all structs, and all user-defined structs) has no proper subtypes, that is, types that convert to it. So we can always bind $T0 to Int in this case.

      I generalized this idea further. Suppose you instead have:

      Int conv $T0

      You can't apply the same optimization here, and just say that $T0 is Int. In Swift, _every_ type has supertypes; those are an optional wrapping the type, the existential types to which this type conforms, and the special standard library AnyHashable type, which is a supertype of every concrete type that conforms to Hashable.

      slava@mathstodon.xyzS This user is from outside of this forum
      slava@mathstodon.xyzS This user is from outside of this forum
      slava@mathstodon.xyz
      wrote last edited by
      #8

      But suppose you also have a conformance constraint, and you know that $T0 must conform to some protocol, say BinaryInteger:

      Int conv $T0
      $T0 conforms BinaryInteger

      Well, existential types cannot conform to protocols in Swift, so we can rule out the possibility that $T0 is existential. It might still be Optional<Int> or AnyHashable, perhaps, but those don't conform to BinaryInteger either.

      So now, we can "prove" that $T0's domain contains exactly one type, and so we can bind $T0 to Int.

      What if you end up in a situation where you're looking at this?

      Int conv $T0
      $T0 conforms Sequence

      Well, Int doesn't conform to Sequence, and neither does Optional<Int> or AnyHashable, so we've made a conflicting set of overload choices; we just haven't realized it yet, because the domain of $T0 is the empty set.

      Now we _do_ realize it, and bind Int to $T0 anyway; this fails the conformance constraint, and in some cases, it fails it much sooner than it would have otherwise, reducing wasted time spent in dead ends.

      slava@mathstodon.xyzS jrose@social.belkadan.comJ 2 Replies Last reply
      0
      • slava@mathstodon.xyzS slava@mathstodon.xyz

        But suppose you also have a conformance constraint, and you know that $T0 must conform to some protocol, say BinaryInteger:

        Int conv $T0
        $T0 conforms BinaryInteger

        Well, existential types cannot conform to protocols in Swift, so we can rule out the possibility that $T0 is existential. It might still be Optional<Int> or AnyHashable, perhaps, but those don't conform to BinaryInteger either.

        So now, we can "prove" that $T0's domain contains exactly one type, and so we can bind $T0 to Int.

        What if you end up in a situation where you're looking at this?

        Int conv $T0
        $T0 conforms Sequence

        Well, Int doesn't conform to Sequence, and neither does Optional<Int> or AnyHashable, so we've made a conflicting set of overload choices; we just haven't realized it yet, because the domain of $T0 is the empty set.

        Now we _do_ realize it, and bind Int to $T0 anyway; this fails the conformance constraint, and in some cases, it fails it much sooner than it would have otherwise, reducing wasted time spent in dead ends.

        slava@mathstodon.xyzS This user is from outside of this forum
        slava@mathstodon.xyzS This user is from outside of this forum
        slava@mathstodon.xyz
        wrote last edited by
        #9

        There are more tricks possible by considering multiple adjacent constraints.

        An existing optimization would compute the join of two types (the least common supertype) in a situation like this:

        Foo conv $T0
        Bar conv $T0

        The implementation of joins() was old and incomplete though and it missed many common situations. I rewrote it to model Swift's subtyping rules faithfully. I got it to compute meets. Unlike joins, where all types are subtypes of the universal Any type, the meet can be empty. For example, suppose that Foo and Bar are classes:

        $T0 conv Foo
        $T0 conv Bar

        By the previous rules, we cannot bind $T0 to Foo or Bar, because Foo and Bar might have proper subtypes (one might be a subclass of the other).

        But if neither is a subclass of the other, then $T0's domain is empty, and we can bind $T0 to Foo or Bar at this point, and backtrack.

        There are a few more combinations, for example if you have this:

        Int conv $T0
        $T0 conv Optional<String>

        There is no type for $T0 that is a supertype of Int but also a subtype of Optional<String>.

        slava@mathstodon.xyzS 1 Reply Last reply
        0
        • slava@mathstodon.xyzS slava@mathstodon.xyz

          There are more tricks possible by considering multiple adjacent constraints.

          An existing optimization would compute the join of two types (the least common supertype) in a situation like this:

          Foo conv $T0
          Bar conv $T0

          The implementation of joins() was old and incomplete though and it missed many common situations. I rewrote it to model Swift's subtyping rules faithfully. I got it to compute meets. Unlike joins, where all types are subtypes of the universal Any type, the meet can be empty. For example, suppose that Foo and Bar are classes:

          $T0 conv Foo
          $T0 conv Bar

          By the previous rules, we cannot bind $T0 to Foo or Bar, because Foo and Bar might have proper subtypes (one might be a subclass of the other).

          But if neither is a subclass of the other, then $T0's domain is empty, and we can bind $T0 to Foo or Bar at this point, and backtrack.

          There are a few more combinations, for example if you have this:

          Int conv $T0
          $T0 conv Optional<String>

          There is no type for $T0 that is a supertype of Int but also a subtype of Optional<String>.

          slava@mathstodon.xyzS This user is from outside of this forum
          slava@mathstodon.xyzS This user is from outside of this forum
          slava@mathstodon.xyz
          wrote last edited by
          #10

          I've got a few more things cooking, but that's enough for now. There are more details in the description for my latest PR, which links to the previous PR, which also links to the one before that: https://github.com/swiftlang/swift/pull/87669

          gregtitus@social.coopG ole@chaos.socialO 2 Replies Last reply
          0
          • slava@mathstodon.xyzS slava@mathstodon.xyz

            But suppose you also have a conformance constraint, and you know that $T0 must conform to some protocol, say BinaryInteger:

            Int conv $T0
            $T0 conforms BinaryInteger

            Well, existential types cannot conform to protocols in Swift, so we can rule out the possibility that $T0 is existential. It might still be Optional<Int> or AnyHashable, perhaps, but those don't conform to BinaryInteger either.

            So now, we can "prove" that $T0's domain contains exactly one type, and so we can bind $T0 to Int.

            What if you end up in a situation where you're looking at this?

            Int conv $T0
            $T0 conforms Sequence

            Well, Int doesn't conform to Sequence, and neither does Optional<Int> or AnyHashable, so we've made a conflicting set of overload choices; we just haven't realized it yet, because the domain of $T0 is the empty set.

            Now we _do_ realize it, and bind Int to $T0 anyway; this fails the conformance constraint, and in some cases, it fails it much sooner than it would have otherwise, reducing wasted time spent in dead ends.

            jrose@social.belkadan.comJ This user is from outside of this forum
            jrose@social.belkadan.comJ This user is from outside of this forum
            jrose@social.belkadan.com
            wrote last edited by
            #11

            @slava > Well, existential types cannot conform to protocols in Swift, so we can rule out the possibility that $T0 is existential.

            except the ones that do (Error and @objc protocols). I wouldn’t be surprised if there are more in the future. Hopefully this isn’t a loadbearing part of the optimizations!

            slava@mathstodon.xyzS 1 Reply Last reply
            0
            • slava@mathstodon.xyzS slava@mathstodon.xyz

              I've got a few more things cooking, but that's enough for now. There are more details in the description for my latest PR, which links to the previous PR, which also links to the one before that: https://github.com/swiftlang/swift/pull/87669

              gregtitus@social.coopG This user is from outside of this forum
              gregtitus@social.coopG This user is from outside of this forum
              gregtitus@social.coop
              wrote last edited by
              #12

              @slava I admire how you can explain this type of thing with such clarity.

              mergesort@macaw.socialM 1 Reply Last reply
              0
              • jrose@social.belkadan.comJ jrose@social.belkadan.com

                @slava > Well, existential types cannot conform to protocols in Swift, so we can rule out the possibility that $T0 is existential.

                except the ones that do (Error and @objc protocols). I wouldn’t be surprised if there are more in the future. Hopefully this isn’t a loadbearing part of the optimizations!

                slava@mathstodon.xyzS This user is from outside of this forum
                slava@mathstodon.xyzS This user is from outside of this forum
                slava@mathstodon.xyz
                wrote last edited by
                #13

                @jrose Of course, but we know which protocols are self-conforming, so we skip those. As long as self-conformance remains "opt-in", it will be fine.

                (In one of the cases where I got a speedup from this, the protocol was SIMD which is unlikely to self-conform in the near future :))

                1 Reply Last reply
                0
                • slava@mathstodon.xyzS slava@mathstodon.xyz

                  I landed some performance improvements for the Swift type checker recently, and I'm currently finishing up the next set of changes which I hope to merge soon.

                  There are two main improvements. The first is a new "disjunction pruning" optimization to help skip impossible overload choices, and the second set of improvements concern implicit conversions and the constraints they generate.

                  ferrix@mastodon.onlineF This user is from outside of this forum
                  ferrix@mastodon.onlineF This user is from outside of this forum
                  ferrix@mastodon.online
                  wrote last edited by
                  #14

                  @slava @joe what do we want? Better performance!

                  When do we want it? Optimally soon.

                  1 Reply Last reply
                  0
                  • gregtitus@social.coopG gregtitus@social.coop

                    @slava I admire how you can explain this type of thing with such clarity.

                    mergesort@macaw.socialM This user is from outside of this forum
                    mergesort@macaw.socialM This user is from outside of this forum
                    mergesort@macaw.social
                    wrote last edited by
                    #15

                    @gregtitus @slava This *type* of thing, amirite?

                    1 Reply Last reply
                    0
                    • slava@mathstodon.xyzS slava@mathstodon.xyz

                      I've got a few more things cooking, but that's enough for now. There are more details in the description for my latest PR, which links to the previous PR, which also links to the one before that: https://github.com/swiftlang/swift/pull/87669

                      ole@chaos.socialO This user is from outside of this forum
                      ole@chaos.socialO This user is from outside of this forum
                      ole@chaos.social
                      wrote last edited by
                      #16

                      @slava Thank you for the great explanation! This went right into my notes.

                      1 Reply Last reply
                      0
                      • slava@mathstodon.xyzS slava@mathstodon.xyz

                        I landed some performance improvements for the Swift type checker recently, and I'm currently finishing up the next set of changes which I hope to merge soon.

                        There are two main improvements. The first is a new "disjunction pruning" optimization to help skip impossible overload choices, and the second set of improvements concern implicit conversions and the constraints they generate.

                        madcoder@infosec.exchangeM This user is from outside of this forum
                        madcoder@infosec.exchangeM This user is from outside of this forum
                        madcoder@infosec.exchange
                        wrote last edited by
                        #17

                        @slava color me naive but do we cache the precious result of a type checker when it was super expensive? It still doesn’t help you with the first time but…

                        1 Reply Last reply
                        1
                        0
                        • R relay@relay.infosec.exchange shared this topic
                        Reply
                        • Reply as topic
                        Log in to reply
                        • Oldest to Newest
                        • Newest to Oldest
                        • Most Votes


                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • World
                        • Users
                        • Groups