Merge "Add documentation for the compose plugin metrics and reports" into androidx-main
diff --git a/compose/compiler/design/compiler-metrics.md b/compose/compiler/design/compiler-metrics.md
new file mode 100644
index 0000000..01cb999
--- /dev/null
+++ b/compose/compiler/design/compiler-metrics.md
@@ -0,0 +1,230 @@
+# Interpreting Compose Compiler Metrics
+
+## Context
+
+The Compose Compiler plugin can generate reports / metrics around certain compose-specific concepts
+that can be useful to understand what is happening with some of your compose code at a fine-grained
+level.
+
+## Enabling Metrics
+
+### AndroidX repository
+
+In the AndroidX repository `./gradlew` accepts options to enable reports.
+
+To enable compiler metrics for a build target include `-Pandroidx.enableComposeCompilerMetrics=true`
+prior to the build target such as:
+
+```
+.gradlew -Pandroidx.enableComposeCompilerMetrics=true :compose:runtime:runtime:compileKotlin
+```
+
+To enable compiler reports for a build target include `-Pandroidx.enableComposeCompilerReports=true`
+prior to the build target such as:
+
+```
+.gradlew -Pandroidx.enableComposeCompilerReports=true :compose:runtime:runtime:compileKotlin
+```
+
+### Other Gradle projects
+
+To enable metrics for a gradle module, include:
+
+```
+compileKotlin {
+    freeCompilerArgs += listOf(
+        "-P",
+        "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=<directory>"
+    )
+}
+```
+
+where `<directory>` is replaced with the location you wish the report written.
+
+To enabled reports for a gradle module, include:
+
+```
+compileKotlin {
+    freeCompilerArgs += listOf(
+        "-P",
+        "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=<directory>"
+    )
+}
+```
+
+where `<directory>` is replaced with the location you wish the report written.
+
+## Reports Breakdown
+
+### Top Level Metrics (<your-module>-module.json)
+
+This report shows some high level metrics that are compose specific. This is mostly to create
+numeric data points which can be tracked over time. The ratios between some of these numbers can be
+interesting: for instance the number of “certainArguments” versus “totalArguments” can give you a
+percentage of arguments to composable calls that have metadata propagated.
+
+Here’s an example of the report for the foundation module:
+
+```
+{
+  "skippableComposables": 53,
+  "restartableComposables": 60,
+  "readonlyComposables": 1,
+  "totalComposables": 100,
+  "restartGroups": 60,
+  "totalGroups": 139,
+  "staticArguments": 25,
+  "certainArguments": 138,
+  "knownStableArguments": 377,
+  "knownUnstableArguments": 25,
+  "unknownStableArguments": 24,
+  "totalArguments": 426,
+  "markedStableClasses": 8,
+  "inferredStableClasses": 28,
+  "inferredUnstableClasses": 0,
+  "inferredUncertainClasses": 0,
+  "effectivelyStableClasses": 36,
+  "totalClasses": 36,
+  "memoizedLambdas": 40,
+  "singletonLambdas": 6,
+  "singletonComposableLambdas": 4,
+  "composableLambdas": 49,
+  "totalLambdas": 81
+}
+```
+
+### Composable Signatures (<your module>-composables.txt)
+
+This report is intended to be consumed by humans, and is printed in pseudo-kotlin style function
+signatures. This report shows every composable function in the module, and breaks down each
+parameter and information about each. This report indicates if the overall composable is
+restartable, skippable, or readonly. Each parameter is marked to be either stable or unstable. And
+each default parameter expression is marked as either static or dynamic.
+
+```
+restartable fun Image(
+  unstable bitmap: ImageBitmap
+  stable contentDescription: String?
+  stable modifier: Modifier? = @static Companion
+  stable alignment: Alignment? = @dynamic Companion.Center
+  stable contentScale: ContentScale? = @dynamic Companion.Fit
+  stable alpha: Float = @static DefaultAlpha
+  stable colorFilter: ColorFilter? = @static null
+)
+```
+
+### Composables Table (<your module>-composables.csv)
+
+This report is a CSV and is intended to be easily thrown into a spreadsheet and digested that way.
+This holds high level metrics specific to every composable function.
+
+### Classes (<your module>-classes.txt)
+
+This report is also meant to be consumed by a human. It is written in pseudo-kotlin style class
+signatures. This file is primarily meant for you to understand how the stable inferencing algorithm
+interpreted a given class. Each class is indicated at the top level as being either stable,
+unstable, or runtime. Runtime means that stability depends on other dependencies which will be
+resolved at runtime (a type parameter or a type in an external module). Stability is determined by
+the fields on the class, so each field is displayed as part of the class, and each field is marked
+as either stable, unstable, or runtime stable as well. The <runtime stability> line at the bottom
+indicates the “expression” that is used to resolve this stability at runtime.
+
+```
+stable class CornerBasedShape {
+  stable val topStart: CornerSize
+  stable val topEnd: CornerSize
+  stable val bottomEnd: CornerSize
+  stable val bottomStart: CornerSize
+  <runtime stability> = Stable
+}
+```
+
+## Things To Look Out For
+
+### Functions that are `restartable` but not `skippable`
+
+In a composables.txt file, you may see some composable functions which are marked as restartable
+but not marked as skippable. These two concepts are closely related, but distinct.
+
+Skippability means that when called during recomposition, compose is able to skip the function if
+all of the parameters are equal. Skippability is often very important for public APIs, and can have
+a big performance impact if the chances of a composable getting called with the same inputs is
+high. The typical reason for a function to not be skippable is when one or more of its parameters
+types are not considered `Stable`.
+
+Restartability means that this function serves as a “scope” where recomposition can start. Any
+function that is skippable must be restartable for correctness, but functions can be restartable
+and not skippable. Though restartability is needed for correctness when a function is skippable, it
+can sometimes be beneficial even if the function is not skippable. If the function reads a `State`
+value during its execution that is very likely to change (for instance, an animated value), then
+restartability is very important, as if it is not restartable, then compose will use an ancestor
+scope to initiate the recomposition when that state value changes.
+
+If you see a function that is restartable but not skippable, it’s not always a bad sign, but it
+sometimes is an opportunity to do one of two things:
+
+  1. Make the function skippable by ensuring all of its parameters are stable
+  2. Make the function not restartable by marking it as a `@NonRestartableComposable`
+
+It is a good idea to do (1) if the function is a highly used public API, and if you think the
+parameters not being stable is an oversight.
+
+It is a good idea to do (2) if the composable function is unlikely to ever be the “root” of a
+recomposition. In other words, if the composable function doesn’t directly read any state variables,
+it is unlikely that this restart scope is ever being used. This can be very difficult for the
+compiler to determine though, so the restart scope is generated anyway unless you specify otherwise
+directly with a `@NonRestartableComposable` annotation.
+
+### Default parameter expressions that are `@dynamic`
+
+Composable functions make heavy use of parameters default expressions. This is an important tool
+that allows a composable to have an API that is both configurable and easy to use. Default
+expressions are capable of executing any code that can be executed in the body of the function, so
+for composable functions, that includes making calls to other composables. It also means that a
+default expression can read a state variable and automatically cause the composable to get
+subscribed to the state.
+
+In order for default parameters to have all of this power, the compose compiler often has to
+generate a fair amount of code around default expressions to make sure they behave in a predictable
+and correct manner. This is only really necessary for composable calls, and for state reads, but it
+is really difficult for the compiler to guarantee that an expression will not perform a state read,
+since state reads can happen almost anywhere.
+
+In the `composables.txt` file, you will see all default parameter value expressions prefixed with
+either `@static` or `@dynamic`. You may find that the compiler is treating it as `@dynamic` even
+though it seems like it should be `@static`. If this is the case, you should strive to make this
+expression `@static` by making it an expression that compose can infer as such. You can do this by
+marking the value as `@Stable`.
+
+Default expressions should be `@static` in every case except for the following two cases:
+
+  1. You are explicitly reading an observable dynamic variable. Composition Locals and state
+     variables are an important example of this. In these cases, you need to rely on the fact that
+     the default expression will be re-executed when the value changes.
+
+  2. You are explicitly calling a composable function, such as `remember`. The most common use case
+     for this is state hoisting.
+
+### Classes that are unstable
+
+In the `classes.txt` file, you may see classes that are unstable. Not all classes need to be stable,
+but a class being stable unlocks a lot of flexibility for the compose compiler to make
+optimizations when a stable type is being used in places, which is why it is such an important
+concept for compose.
+
+The compose compiler will infer whether or not a given class is stable or not at compile time, and
+its algorithm for doing that is not all that complicated. The compiler will look at all of the
+fields on the class, and it cannot be inferred as stable if any one of the fields is:
+
+  1. The field is mutable (it is associated with a `var` property)
+  2. The field has a non-stable type
+
+If any one of the fields on the class meets this criteria, it will be inferred as `unstable`, but
+that doesn’t mean that it can’t be marked as stable. Sometimes mutable fields are used in ways that
+are still safe in the context of Stability guarantees. For instance, a very common case is to use a
+field to “cache” the result of some calculation. If the caching is only done for performance
+reasons, and the public API of that class makes it impossible to know whether the value is “cached”
+or not, then the class could still be marked stable.
+
+In order to find the field(s) which are causing a class to be unstable, you simply need to look for
+any field in the class that is a `var` or an `unstable val`.