Hello, I am happy to announce that from the latest Scala Next nightly (3.5.0-RC1-bin-20240411-4f17e25-NIGHTLY) you can now try pipelined builds in sbt.
[Edit 11 April] - updated post with a newer nightly with better diagnostics for problematic macro code under pipelining.
As shown in some benchmarks from my Scala Days 2023 talk, projects such as lichess-org/lila could reduce build times by 25% from a single setting change.
As a reminder you need sbt > 1.4, and to see any difference, you need at least two âscopesâ to build - that can be test scope and main scope on a single project, or two subprojects.
you also need to set ThisBuild / usePipelining := true.
Try it out! (also you can try it in Scala 2 projects since last few years)
Thanks to everyone whose work made this possible!
Note: as far as I know only sbt supports pipelined builds, not bloop or Mill. (other tools not verified)
[error] Cyclic macro dependencies in ...
[error] Compilation stopped since no further progress can be made.
[error]
[error] To fix this, place macros in one set of files and their callers in another.
[error]
[error] Compile with -Xprint-suspension for information.
[error] one error found
java.util.concurrent.CancellationException
What are the known limitations? Was there an attempt to run the OpenCB with pipelining on for all projects?
I have a test specifically where macros cause a suspension, including in both inlining and typer phases:
Cyclic macro dependencies in âŚ
Iâd like to see which library this was, because this shouldnât cause more suspensions than already are present.
Edit: Also it seems it is quite important how you set the ThisBuild/usePipelining setting to avoid problems. For best results write it directly in the build.sbt file
Edit: we are now running one - it seems there are projects that raise new problems not seen previously
Edit 2: a lot of those errors seem like misconfiguration of the build when running
@soronpo you should be able to fix the problem by turning off pipelining in compiler_ir
lazy val compiler_ir = (project in file("compiler/ir"))
.settings(
name := s"$projectName-compiler-ir",
- settings
+ settings,
+ Compile / exportPipelining := false
).dependsOn(internals)
you can see an explanation here, where I implement a better error message:
Edit 1: there should be a better error message in the next nightly (releasing 11th April)
Edit 2: now released at 3.5.0-RC1-bin-20240411-4f17e25-NIGHTLY
in general, all transitive dependencies of a macro should not be pipelined (and sbt automatically turns off pipelining on a project with macro definitions). UNLESS you never call the macro within any project (you probably are if you have adequate tests).
This unfortunately means that for a lot of projects, you canât just âflip the switchâ and hope it to work. In this case its best to have macros as close to the top of the root of the build as possible
As of April 17th the new nightly (3.5.0-RC1-bin-20240416-63810dc-NIGHTLY) will write pipeline outputs in parallel with the main compiler thread, this should give a (very) small boost to performance.
(previously we blocked the main compiler thread until these pipeline outputs were written)
It fails to build my work project that has a subproject cross-compiling Java and Scala.
The compiler emits [E008] Not Found Error for Java classes even under compileOrder := CompileOrder.JavaThenScala config. I guess it tries to compile Scala code before Java compilation is done.
For a project with 23KLOC my compile time goes up from 46s (3.4.1) to 54s (3.5.0-RC1-bin-20240415-c47138c-NIGHTLY). (Both are second compilations after clean). This is only one experiment, more are needed to be certain this is a real effect of course. But, how can you verify that the pipelining is really active? I see no difference in the messages.
I performed stb followed by five cycles of clean, compile with and without pipelining active. The first value should be discarded as it is influenced by loading caches etc. Also, i made sure i quickly repeated the commands, thereby keeping the core temperature as stable as possible.
Results:
with pipelining: 72s, 44s, 40s, 38s, 37s.
without pipelining: 67s, 44s, 41s, 40s, 39s.
So pipelining seems to speed up the process marginally. I should do more experiments to see if this survives the test of statistical significance, but its an indication alright.
pipelining benefits most when you have many levels of (edit: subproject) dependencies - do you have a description of the projects layout? Also it canât do much if you have not enough cpu cores compared to the number of projects that compile concurrently.
Also, if a project has macros, then sbt will turn off pipelining invisibly.
Iâm thinking of adding an extra flag you can turn on that will hard error any time pipelining is probably not effectively used.