Optimistic `cargo run` for faster compile/iteration times

The best optimization is if you don't have to do something, but I think we can all agree that there is not much that can/should be removed. We do want a lot of compile time checks.

But what if we could do these later [1]. For fast iteration you want to get your application running/compiled quickly, but you don't necessarily need all of the checks immediately. In theory the following could result in faster iteration times at the cost of getting compile errors AFTER the application starts (and potential Undefined Behavior by running code that is not correct/sound):

  • Keep cargo build as is, there isn't much that can be done there
  • For cargo run (probably behind an opt-in cli flag)
    • Current behavior (simplified, order might not be quite right): Check syntax, HIR/MIR, Check lifetimes, MIR/IR, codegen, execute
    • Suggestion: Check syntax, HIR, MIR, IR, codegen, in_parallel(execute, Check lifetimes); abort and/or notify if there are problems detected after the application started.

This way you might be able to get it running faster, can start testing and get notified afterwards if it turns out the binary you've been running has problems. But for things like fast iteration on application logic you might not even cause issues this that result in UB.

Does it help in practice: I don't know (linking can be quite slow and I don't know how much of checking is required for codegen if you accept UB on lifetime errors).

Is it doable and should we do it: I don't know enough compiler internals


  1. Yes, that's likely very hard and there are almost certainly dependencies between these tasks. â†Šī¸Ž

Does it help in practice: I don't know (linking can be quite slow and I don't know how much of checking is required for codegen if you accept UB on lifetime errors).

One way to check is to see where you time is going today, including using

If most of the time is spent in backend/codegen and linking, then small gains like skipping checks won't have much of an impact.

8 Likes

Even assuming this were something wanted, I'm 99% sure that the time checking lifetimes is negligible compared to everything else.

8 Likes

Type checking can be complex sometimes (though nowhere near the cost of codegen, typically), but also you need to know the types of things in order to compile them so skipping it isn't possible anyway.

Overall I think you'll find that most of the cost is in monomorphic codegen, not in the checks, so the work is not worth it. Especially since the maintenance burden of weird things getting deeper into the compiler is already bad now (MIR sometimes sees invalid types and such) and letting everything just YOLO through all the phases would make that way worse, which I really don't think we should do.


Note that I think there's still work to be done to skip lints entirely if they're disabled, or maybe even with a particular flag. So if you wanted to work on that it'd be appreciated, even if it probably doesn't actually save that much time. (Or maybe late lints could be run in parallel with codegen? Dunno how hard that would be to do, practically.)

5 Likes

The borrow checker is also needed for compilation due to it inferring the types of opaque types. And yeah, its performance is, while not instant, not that big of a deal.

3 Likes

I don't think that is true. Borrowck is necessary to infer the lifetimes of opaque types, but I've got a branch somewhere that bypasses borrowck and it can compile the standard library just fine. It does ICE on some tests that should fail borrowck however.

True, but if we erase all lifetimes, we can just use the opaques from typeck.