Live data from Hacker News

The Swift compiler is slow due to how types are inferred

danielchasehooper.com

151–160 of 229 posts

Re: The Swift compiler is slow due to how types are inferred

#151

Earlier quoted context omitted.

I'd trade type inference for expressibility (function and operator overloading in particular). But this seems to currently be a heretical opinion.

Rust took the other road: error[E0277]: cannot add `i64` to `i32` 1i32 + 2i64; ^ no implementation for `i32 + i64` It bothered me at first, there are a lot of explicit annotations for conversions when dealing with mixed precision stuff. But I now feel that it was the exactly correct choice, and not just because it makes inference easier.

Swift does that for integers, floats and doubles, too. https://docs.swift.org/swift-book/documentation/the-swift-pr...:

“Conversions between integer and floating-point numeric types must be made explicit:

  let three = 3
  let pointOneFourOneFiveNine = 0.14159
  let pi = Double(three) + pointOneFourOneFiveNine
  // pi equals 3.14159, and is inferred to be of type Double

Re: The Swift compiler is slow due to how types are inferred

#152
post #32

It's really hard for me to read past Lattner's quote. "Beautiful minimal syntax" vs "really bad compile times" and "awful error messages". I know it's not helpful to judge in hindsight, lots of smart people, etc. But why on earth would you make this decision for a language aimed at app developers? How is this not a design failure? If I read this article correctly, it would have been an unacceptable decision to make u…

Honestly it follows the design of the rest of the language. An incomplete list: 1. They wrote it to replace C++ instead of Objective-C. This is obvious from hearing Lattner speak, he always compares it to C++. Which makes sense, he dealt with C++ every day, since he is a compiler writer. This language does not actually address the problems of Objective-C from a user-perspective. They designed it to address the proble…

great context. the whole narrative you present kind of begs the question of whether swift could actually be a good systems language.

as a SwiftUI app dev user I feel like this (and the OP's post) lines up with my experience but I've never tried it for e.g. writing an API server or CLI tool.

Re: The Swift compiler is slow due to how types are inferred

#153

I'm suspicious about the truth of this claim. I don't think bidirectional typechecking is the problem in itself: the problem trying to do type inference when you have some extremely flexible operator overloading, subtyping, and literal syntax features. It's these "expressive" features which have made type inference in Swift far more computationally complex, not type inference itself. And certainly not bidirectional t…

I found an online Swift Playground, and here is a smaller test case that produces the "unable to type-check this expression in reasonable time" message:

  print("a" + "b" + "c" + 11 + "d")
...if you reduce it even further to:

  print("a" + "b" + "c" + 11)
...the error message is:

  :3:23: error: binary operator '+' cannot be applied to operands of type 'String' and 'Int'
  print("a" + "b" + "c" + 11)
        ~~~~~~~~~~~~~~~ ^ ~~
  :3:23: note: overloads for '+' exist with these partially matching parameter lists: (Int, Int), (String, String)
...it still falls down on with the "reasonable time" error on this tiny example, even if you fully specify the types:

  let a:String = "a"
  let b:String = "b"
  let c:String = "c"
  let d:String = "d"
  let e:String = "e"
  let n:Int = 11
  
  print(a + b + c + n + d + e)
...is that pointing to a problem with type-checking or overload resolution more than type inference? Is there a way in Swift to annotate the types of sub-expressions in-line, so you could try something like:

  print((a + b + c):String + n + d + e)

Re: The Swift compiler is slow due to how types are inferred

#154
post #8
post #3

This is not a fixable flaw. Solving these constraints efficiently can definitely get you a Turing award, it's basically the SAT problem. And without this type system, swift is just Objective C in a prettier syntax, so Apple has to bite the bullet and bear with it.

It sort of is fixable, though. If you think about it, the problem is a bunch of functions are all mapped to one of a small set of names: +*/, etc. That is, the operators. If we didn't try to cram all this functionality into a tiny handful of symbols because of some weak analogy they have with basic math operations[1], then the compiler would have far fewer name conflicts to try to disambiguate, and the problem goes a…

So you’re saying one potential solution is to use the APL character set?

Re: The Swift compiler is slow due to how types are inferred

#155

Earlier quoted context omitted.

Honestly it follows the design of the rest of the language. An incomplete list: 1. They wrote it to replace C++ instead of Objective-C. This is obvious from hearing Lattner speak, he always compares it to C++. Which makes sense, he dealt with C++ every day, since he is a compiler writer. This language does not actually address the problems of Objective-C from a user-perspective. They designed it to address the proble…

This is a great comment, you clearly know what you're talking about and I learned a lot. I wanted to push back on this a bit: > The "Objective-C problems" they fixed were things that made Objective-C annoying to optimize, not annoying to write (except if you are a big hater of square brackets I suppose). From an outsider's perspective, this was the point of Swift: Objective C was and is hard to optimize. Optimal code…

Let me preface this by saying performance is complicated, and unfortunately far more... religious than you'd expect. A good example of this is the Objective-C community’s old insistence that good performance was incompatible with garbage collection, despite decades of proof otherwise. This post [1] about Swift getting walloped by node.js is fascinating in seeing how a community responds with results that challenge their expectations (“what do you expect, the JS BigInt is optimized and ours isn’t”, “It’s slower, but uses less RAM!”, etc.). As it turns out, questions like GC vs. reference counting, often end up being much more nuanced (and unsatisfactory) than which is simply "faster". You end up with far more unsatisfying conclusions like one is more deterministic but often slower (and as it turns out most UIKit apps aren’t realtime systems).

All this to say, it is hard to answer this question in one comment, but to try to sum up my position on this, I believe the performance benefits of Swift were and remain overblown. It’s a micro benchmark based approach, which as we’ll see in a second is particularly misguided for Swift's theoretically intended use case as an app language. I think increasingly people agree with this as they haven't really found Swift to deliver on some amazing performance that wouldn’t have been possible in Objective-C. This is for a number of reasons:

1. As mentioned above, the most important flaw with a performance based Swift argument is that the vast majority of the stack is still written in Objective-C/C/etc.. So even if Swift was dramatically better, it’s only usually affecting your app’s code. Oftentimes the vast majority of the time is spent in framework code. Think of it this way: pretend that all of iOS and UIKit were written in JavaScript, but then in order to “improve performance” you write your app code in C. Would it be faster? I guess, but you can imagine why it may not actually end up having that much of an effect. This was ironically the bizarre position we found ourselves in with Swift: your app code was in a super strict typed language, but the underlying frameworks were written in a loosey-goosey dynamic language. This is exact opposite of how you'd want to design a stack. Just look at games, where performance is often the absolute top priority: the actual game engine is usually written in something like C++, but then the game logic is often written in a scripting language like Lua. Swift iOS apps are the reverse of this. Now, I'm sure someone will argue that the real goal is for the entire stack to eventually be in Swift, at which point this won't be an issue anymore, but now we're talking about a 20-year plan, where it seems weird to prioritize my Calculator app's code as the critical first step.

2. As it turns out, Objective-C was already really fast! Especially since, due to its ability to trivially interface with C and C++, a lot of existing apps in the wild had already probably topped out on performance. This wasn't like you were taking an install base of python apps and getting them all to move over to C. This was an already low-level language, where many of the developers were already comfortable with the "performance kings" of the C-family of languages. Languages, which, for the record, have decades of really really good tooling specifically to make things performant, and decades of engineering experience by their users to make things performant. And so, in practice, for existing apps, this often felt more like a lateral move. I actually remember feeling confused when after the announcement of Swift people started talking about Objective-C as if it was some slow language or something. Like, literally the year before, Objective-C was considered the low-level performance beast compared to, say, Android's use of Java. Objective-C just wasn't that that slow of a comparison point to improve that much on. The two languages even share the same memory management model (something that ends up having a big affect on its performance characteristics). Dynamic dispatch (objc_msgSend) just does not really end up really dominating your performance graph when you profile your app.

3. But perhaps most importantly, I think there is a mirror misguided focus on language over frameworks as with the developer ergonomics issues I pointed out above. If you look at where the actual performance gains have come from in apps, I’d argue that it’s overwhelmingly been from conceptual framework improvements, not tiny language wins. A great example of this is CoreAnimation. Making hardware accelerated graphics accessible through a nice declarative API, such that we can move as much animation off the CPU and onto the GPU as possible, is one of the key reasons everything feels so great on iOS. I promise no language change will make anywhere near as big of a dent as Apple's investment in CoreAnimation did. I’d argue that if we had invested development time in, e.g., async/await in Objective-C, rather than basically delaying that work for a decade in Swift, we’d very possibly be in a much more performant world today.

Anyways, these are just a few of thoughts on the performance-side of things. Unfortunately, as time moves on, now a decade into this transition, while I find more people agreeing with me than, say, when Swift was first announced, it also becomes more academic since it's not like Apple is going to go back and try to make Objective-C 3 or something now. That being said, I do think it is still useful to look back and analyze these decisions, to avoid making similar mistakes in the future. I think the Python 2 to 3 transition provided an important lesson to other languages, I hope someday we look at the Swift introduction as a similar cautionary tale of programming language design and community/ecosystem stewardship and management.

1. https://forums.swift.org/t/standard-vapor-website-drops-1-5-...

Re: The Swift compiler is slow due to how types are inferred

#156
post #45
post #3

This is not a fixable flaw. Solving these constraints efficiently can definitely get you a Turing award, it's basically the SAT problem. And without this type system, swift is just Objective C in a prettier syntax, so Apple has to bite the bullet and bear with it.

I know that historically, we were taught that NP-complete problems are unsolvable for interesting problem sizes, but that was already a bit of lie in when I went to university. (TSP was presented as a warning example, but even back then, very efficient approximations existed.) These days, when you have a SAT-like problem, you're often done because you can throw a SAT solver at it, and it will give you an answer in a…

I know, my PhD was about verifying software correctness with SMT solvers.

The reason we don't do this in production is that the solvers take an unpredictable amount of time. A small problem can take forever and a large problem can be instant. You can't gave that in a compiler.

There are enough people at Apple who know about SAT solvers. :)

Re: The Swift compiler is slow due to how types are inferred

#157
post #8
post #3

This is not a fixable flaw. Solving these constraints efficiently can definitely get you a Turing award, it's basically the SAT problem. And without this type system, swift is just Objective C in a prettier syntax, so Apple has to bite the bullet and bear with it.

It sort of is fixable, though. If you think about it, the problem is a bunch of functions are all mapped to one of a small set of names: +*/, etc. That is, the operators. If we didn't try to cram all this functionality into a tiny handful of symbols because of some weak analogy they have with basic math operations[1], then the compiler would have far fewer name conflicts to try to disambiguate, and the problem goes a…

It's more about potential protocol conformance than overloading.

If you discard that, we are back to Objective C.

Re: The Swift compiler is slow due to how types are inferred

#158
post #3

This is not a fixable flaw. Solving these constraints efficiently can definitely get you a Turing award, it's basically the SAT problem. And without this type system, swift is just Objective C in a prettier syntax, so Apple has to bite the bullet and bear with it.

Both your points can be addressed by: It's not a choice between infer everything and infer nothing.

It's worse that infer everything.

It's infer everything and make sure there's only one possible interpretation, with certain exceptions.

Re: The Swift compiler is slow due to how types are inferred

#159

I'm suspicious about the truth of this claim. I don't think bidirectional typechecking is the problem in itself: the problem trying to do type inference when you have some extremely flexible operator overloading, subtyping, and literal syntax features. It's these "expressive" features which have made type inference in Swift far more computationally complex, not type inference itself. And certainly not bidirectional t…

Yeah, I agree. Regular H-M typechecking if you don't have subtyping is actually really fast. The problem is that subtyping makes it really complicated--in fact, IIRC, formally undecidable.

It's funny because it's doubly exponential WRT code size. However it's also almost linear (n log*(n) due to union-find) WRT the size of the type and sane humans don't write programs with huge types.

Re: The Swift compiler is slow due to how types are inferred

#160

One of the interesting tradeoffs in programming languages is compile speed vs everything else. If you've ever worked on a project with a 40 minute build (me) you can appreciate a language like go that puts compilation speed ahead of everything else. Lately I've been blown away by the "uv" package manager for Python which not only seems to be the first correct one but is also so fast I can be left wondering if it real…

> If you've ever worked on a project with a 40 minute build

I've worked on plenty of C++ code based that had a 2 day build time!

If you were lucky, incremental builds only took a few hours.

Post reply on HN