Skip to content

Commit 7b61ed7

Browse files
authored
Merge pull request #910 from ashawley/fix-scalac-profiling-post
Fix scalac-profiling post
2 parents 533b459 + 5462526 commit 7b61ed7

File tree

1 file changed

+31
-29
lines changed

1 file changed

+31
-29
lines changed

blog/_posts/2018-06-04-scalac-profiling.md

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
layout: blog-detail
33
post-type: blog
44
by: Jorge Vicente Cantero
5-
title: "Speeding Up Compilation Time with `scalac-profiling`"
5+
title: "Speeding Up Compilation Time with scalac-profiling"
66
---
77

88
In this blog article, I’d like to introduce you to a new tool that we’ve
@@ -11,10 +11,10 @@ better understand what is slowing down compilation in your project.
1111

1212
It turns out that the use of Scala's implicits as well as macros can greatly
1313
increase compilation times, depending on how they are used and how your
14-
codebase is organized. Codebases that make use of libraries like Shapeless,
14+
codebase is organized. Codebases that make use of libraries like [Shapeless],
1515
or which rely on typeclass derivation, may be particularly prone to these
16-
slow-downs. (As of Scala 2.12, typeclass derivation is based on
17-
implicitly-triggered macro expansions.)
16+
slow-downs. As of Scala 2.12, typeclass derivation is based on
17+
implicitly-triggered macro expansions.
1818

1919
The goal of this blog post is to help you understand when these things are
2020
happening in your code, so you can remove code that triggers unnecessary
@@ -82,7 +82,7 @@ it should compile in ~4 seconds.
8282
`frontend` depends on
8383
[`case-app`](https://github.com/alexarchambault/case-app/), a command-line
8484
parsing library for Scala that uses
85-
[Shapeless](https://github.com/milessabin/shapeless). This library is
85+
[Shapeless]. This library is
8686
excellent, but slows our compilation down to 30 seconds.
8787

8888
Waiting 30 seconds for a change to take effect (even under incremental
@@ -194,9 +194,9 @@ Add the compiler flag to the field `options` inside the
194194
automatically pick up your changes and add the compiler option without the
195195
need of a `reload`.
196196

197-
(If you use sbt, add `scalacOptions in Compile += "-Ystatistics"` to your
197+
If you use sbt, add `scalacOptions in Compile += "-Ystatistics"` to your
198198
project settings. If you want to profile tests scope it to `Test` instead of
199-
`Compile`.)
199+
`Compile`.
200200

201201
Run `bloop compile frontend -w --reporter scalac` (we use the default scalac
202202
reporter for clarity) and have a look at the data. The output of the
@@ -270,7 +270,7 @@ following counters:
270270
```
271271

272272
The Scala compiler creates almost two million class symbols (!) and
273-
typechecks 134734 identifiers, almost the double of selections and half of
273+
typechecks 134734 identifiers, almost double the selections and half of the
274274
applications. Those are pretty high values. That begs the question: why are
275275
we creating so many classes?
276276

@@ -290,7 +290,7 @@ sequence of types -- also called finding "least upper bound" among some
290290
types). Eugene Yokota explains it well [in this well-aged blog
291291
post](http://eed3si9n.com/stricter-scala-with-ynolub).
292292

293-
`time spent in findmember` and it's sister `time spent in findmembers` should
293+
`time spent in findmember` and its sister `time spent in findmembers` should
294294
be up in the profiles whenever you have deep class hierarchies and lots of
295295
overridden methods.
296296

@@ -300,9 +300,9 @@ dependent types, type projections or abstract types in a more general way.
300300
In the case of `frontend`, the durations of all these operations are
301301
reasonable, which hints us that the inefficiency is elsewhere.
302302

303-
(For most of the cases, these timers are unlikely to be high when typechecking
303+
For most of the cases, these timers are unlikely to be high when typechecking
304304
your program. If they are, try to figure out why and file a ticket in
305-
`scala/bug` so that either I or the Scala team can look into it.)
305+
[Scala compiler's issue tracker](https://github.com/scala/bug/) so that either I or the Scala team can look into it.
306306

307307
### The troublemaker
308308

@@ -561,7 +561,7 @@ typeclass derivation. When used in that context, it is common that the compiler
561561
repeats the materialization of implicit instances. This is the main source of
562562
inefficiencies.
563563
564-
Travis Brown explains these inefficiences well in a more high-level manner in
564+
Travis Brown explains these inefficiencies well in a more high-level manner in
565565
[this talk about Generic
566566
Derivation](https://meta.plasm.us/slides/scalaworld/#65) at Scalaworld.
567567
@@ -586,7 +586,7 @@ implicitly[Encoder[Foo]]
586586
implicitly[Encoder[Bar]]
587587
```
588588
589-
For example, the code above illustrates how an hypothetic `Encoder` typeclass
589+
For example, the code above illustrates how a hypothetical `Encoder` typeclass
590590
would need to materialize `List[String]` **twice** since its type appears in
591591
both definitions. The second call cannot detect that the first one
592592
synthesizes `Encoder[List[String]]` because for all purposes there isn't an
@@ -596,8 +596,8 @@ The same happens in nested implicit searches and it worsens as more and more
596596
functional dependencies generated by macros and nested types are used and
597597
required in every step of the inductive process.
598598
599-
Remember that `frontend` was expanding 44500 macros and how intense was
600-
typechecking? Well, now we have a faint idea why.
599+
Remember that `frontend` was expanding 44500 macros and how intense
600+
typechecking was? Well, now we have a faint idea why.
601601
602602
603603
## The quest for optimization
@@ -706,7 +706,7 @@ turn handy whenever you research on your own. Check the rest of the supported
706706
compiler plugin flags [in the code](https://github.com/scalacenter/scalac-profiling/blob/master/plugin/src/main/scala/ch/epfl/scala/ProfilingPlugin.scala#L33-L40).
707707
708708
After this short intro, let's delve into the data. The first thing that
709-
strucks me is how similar all the towers of implicits look (both in shape and
709+
struck me is how similar all the towers of implicits look (both in shape and
710710
duration). If we hover over all the chunks, the repetition will be obvious;
711711
most bite-sized chunks materialize either `Parser[CommonOptions]` or
712712
`Parser[CliOptions]`, depending at the height of the implicit branch we look
@@ -753,7 +753,7 @@ Great! Well, let's check the compile time and flamegraphs now.
753753
The compile time is 2.5x faster. Not bad for a two line change. The duration
754754
of implicit search accounts for 13 seconds, roughly ~95% of typer.
755755
756-
The flamegraph has slim down and doesn't contain the successful implicit
756+
The flamegraph has slimmed down and doesn't contain the successful implicit
757757
searches for `Parser[CommonOptions]` and `Parser[CliOptions]`. However, there
758758
seems to be quite a few of failed implicit searches that trigger unnecessary
759759
macro expansions that are afterwards discarded because their type doesn't
@@ -771,16 +771,16 @@ when finding an implicit for `HListParser` (which takes type parameters
771771
[inferred from its other functional
772772
dependencies](https://github.com/alexarchambault/case-app/blob/v1.2.0/core/shared/src/main/scala/caseapp/core/Parser.scala#L77-L84)).
773773
774-
Let's further debug this with `-Xlog-implicits` (by adding it to the scalac
775-
options of the bloop configuration file).
774+
Let's further debug this by adding `-Xlog-implicits` to the scalac
775+
options of the bloop configuration file.
776776
777-
(This is a good moment to try to minimize the problem. `-Xlog-implicits` will
777+
This is a good moment to try to minimize the problem. `-Xlog-implicits` will
778778
log a lot of failed searches and we want to be able to see through the noise.
779779
I did minimise the issue
780780
[here](https://github.com/scalacenter/scalac-profiling/pull/23/commits/dbcb8d480e9b402899d21620055bc555b2841382).
781-
Doing `implicitly[Parser[CliOptions]]` also reproduces it.)
781+
Doing `implicitly[Parser[CliOptions]]` also reproduces it.
782782
783-
Among all the logs, this is the one that calls most my attention.
783+
Among all the logs, this is the one that attracts my attention the most.
784784
785785
```
786786
/data/rw/code/scala/loop/frontend/src/main/scala/bloop/cli/CliParsers.scala:48:37: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[bloop.cli.CommonOptions,V] because:
@@ -818,7 +818,7 @@ and
818818
The problem of incorrect instantiated type arguments we saw before seems
819819
specific to the way the compiler carries out the implicit search. Fixing it
820820
requires most likely changes to the implicit search algorithm, as [a similar
821-
scala/bug did](https://github.com/scala/bug/issues/10528). I tried porting
821+
Scala compiler issue](https://github.com/scala/bug/issues/10528) did. I tried porting
822822
these changes to 2.12.x and use `-Xsource:2.13` but the failed macro
823823
expansions didn't go away.
824824
@@ -943,7 +943,7 @@ and the returned refinement type from the macro was `Option[String] ::
943943
this.P`.
944944
945945
We can try to debug and expand all type parameters, see what we get and
946-
continue the exploration from there. But whenever we find such a misterious
946+
continue the exploration from there. But whenever we find such a mysterious
947947
open-ended error, it's difficult to pinpoint what the real problem and fix
948948
should be.
949949
@@ -1006,12 +1006,12 @@ experienced with the codebase have a look at it. If we're lucky, someone will
10061006
fix this issue upstream soon and we'll benefit from this speed up when we
10071007
upgrade.
10081008
1009-
(After discussing this issue with [Miles](https://github.com/milessabin/) we
1009+
After discussing this issue with the author of Shapeless, [Miles Sabin](https://github.com/milessabin/), we
10101010
both agree the strict/lazy macro is not handling refinement types correctly
10111011
and that this performance penalty is a bug. This bug will most likely be fixed
10121012
in a future version of Shapeless after 2.3.3 for all its users. Some of these
10131013
performance implications will be gone with Scala 2.13, that adds by-name
1014-
implicits to the compiler.)
1014+
implicits to the compiler.
10151015
10161016
#### Deduplicating more expansions
10171017
@@ -1243,8 +1243,8 @@ Done compiling.
12431243
It is safe to say it out loud now: we have reduced compilation time from 32.5
12441244
seconds to 4 seconds. That's an **8x reduction in our compile time**.
12451245
1246-
A great result taking into account that we've only modified around 30 lines
1247-
of code.
1246+
A great result taking into account that we've only [modified around 30 lines
1247+
of code in Bloop](https://github.com/scalacenter/bloop/pull/509).
12481248
12491249
## Conclusion
12501250
@@ -1294,6 +1294,8 @@ knowledge to invalidate caching depending on the kind of macro and call-site.
12941294
There's a bright future ahead of us and we are working hard to get there.
12951295
12961296
In the meanwhile, this blog post aims to provide all the possible data to
1297-
alleaviate the compile times of users that leverage automatic typeclass
1297+
alleviate the compile times of users that leverage automatic typeclass
12981298
derivation. I hope this blog post helps make your team more productive with
12991299
Scala.
1300+
1301+
[Shapeless]: https://github.com/milessabin/shapeless

0 commit comments

Comments
 (0)