@@ -4,13 +4,11 @@ package com.squareup.benchmark.composeworkflow.benchmark
44
55import androidx.benchmark.junit4.BenchmarkRule
66import androidx.benchmark.junit4.measureRepeated
7- import androidx.compose.runtime.Composable
87import androidx.test.ext.junit.runners.AndroidJUnit4
98import com.squareup.workflow1.RuntimeConfigOptions
109import com.squareup.workflow1.Workflow
1110import com.squareup.workflow1.WorkflowExperimentalApi
1211import com.squareup.workflow1.WorkflowExperimentalRuntime
13- import com.squareup.workflow1.compose.ComposeWorkflow
1412import com.squareup.workflow1.compose.composable
1513import com.squareup.workflow1.compose.renderChild
1614import com.squareup.workflow1.renderChild
@@ -26,6 +24,8 @@ import org.junit.Test
2624import org.junit.runner.RunWith
2725import kotlin.test.assertEquals
2826
27+ private const val MaxChildCount = 100
28+
2929@OptIn(WorkflowExperimentalRuntime ::class )
3030@RunWith(AndroidJUnit4 ::class )
3131class ComposeWorkflowMicroBenchmark {
@@ -34,38 +34,89 @@ class ComposeWorkflowMicroBenchmark {
3434
3535 @Test fun tradRoot_tradChildren_initialRender () {
3636 benchmarkSimpleTreeInitialRender(
37- maxChildCount = 100 ,
3837 composeRoot = false ,
3938 composeChildren = false
4039 )
4140 }
4241
4342 @Test fun tradRoot_composeChildren_initialRender () {
4443 benchmarkSimpleTreeInitialRender(
45- maxChildCount = 100 ,
4644 composeRoot = false ,
4745 composeChildren = true
4846 )
4947 }
5048
5149 @Test fun composeRoot_tradChildren_initialRender () {
5250 benchmarkSimpleTreeInitialRender(
53- maxChildCount = 100 ,
5451 composeRoot = true ,
5552 composeChildren = false
5653 )
5754 }
5855
5956 @Test fun composeRoot_composeChildren_initialRender () {
6057 benchmarkSimpleTreeInitialRender(
61- maxChildCount = 100 ,
58+ composeRoot = true ,
59+ composeChildren = true
60+ )
61+ }
62+
63+ @Test fun tradRoot_tradChildren_tearDown () {
64+ benchmarkSimpleTreeTearDown(
65+ composeRoot = false ,
66+ composeChildren = false
67+ )
68+ }
69+
70+ @Test fun tradRoot_composeChildren_tearDown () {
71+ benchmarkSimpleTreeTearDown(
72+ composeRoot = false ,
73+ composeChildren = true
74+ )
75+ }
76+
77+ @Test fun composeRoot_tradChildren_tearDown () {
78+ benchmarkSimpleTreeTearDown(
79+ composeRoot = true ,
80+ composeChildren = false
81+ )
82+ }
83+
84+ @Test fun composeRoot_composeChildren_tearDown () {
85+ benchmarkSimpleTreeTearDown(
86+ composeRoot = true ,
87+ composeChildren = true
88+ )
89+ }
90+
91+ @Test fun tradRoot_tradChildren_subsequentRender () {
92+ benchmarkSimpleTreeSubsequentRender(
93+ composeRoot = false ,
94+ composeChildren = false
95+ )
96+ }
97+
98+ @Test fun tradRoot_composeChildren_subsequentRender () {
99+ benchmarkSimpleTreeSubsequentRender(
100+ composeRoot = false ,
101+ composeChildren = true
102+ )
103+ }
104+
105+ @Test fun composeRoot_tradChildren_subsequentRender () {
106+ benchmarkSimpleTreeSubsequentRender(
107+ composeRoot = true ,
108+ composeChildren = false
109+ )
110+ }
111+
112+ @Test fun composeRoot_composeChildren_subsequentRender () {
113+ benchmarkSimpleTreeSubsequentRender(
62114 composeRoot = true ,
63115 composeChildren = true
64116 )
65117 }
66118
67119 private fun benchmarkSimpleTreeInitialRender (
68- maxChildCount : Int ,
69120 composeRoot : Boolean ,
70121 composeChildren : Boolean
71122 ) = runTest {
@@ -91,9 +142,91 @@ class ComposeWorkflowMicroBenchmark {
91142 assertEquals(0 , renderings.value.rendering)
92143 }
93144
94- props.value = RootWorkflowProps (childCount = maxChildCount, composeChildren = composeChildren)
145+ props.value = RootWorkflowProps (childCount = MaxChildCount , composeChildren = composeChildren)
146+ testScheduler.runCurrent()
147+ assertEquals(MaxChildCount , renderings.value.rendering)
148+ }
149+
150+ workflowJob.cancel()
151+ }
152+
153+ private fun benchmarkSimpleTreeTearDown (
154+ composeRoot : Boolean ,
155+ composeChildren : Boolean
156+ ) = runTest {
157+ val props =
158+ MutableStateFlow (RootWorkflowProps (childCount = 0 , composeChildren = composeChildren))
159+ val workflowJob = Job (parent = coroutineContext.job)
160+ val renderings = renderWorkflowIn(
161+ workflow = if (composeRoot) {
162+ composeSimpleRoot
163+ } else {
164+ traditionalSimpleRoot
165+ },
166+ props = props,
167+ scope = this + workflowJob,
168+ runtimeConfig = RuntimeConfigOptions .ALL ,
169+ onOutput = {}
170+ )
171+
172+ benchmarkRule.measureRepeated {
173+ runWithTimingDisabled {
174+ props.value =
175+ RootWorkflowProps (childCount = MaxChildCount , composeChildren = composeChildren)
176+ testScheduler.runCurrent()
177+ assertEquals(MaxChildCount , renderings.value.rendering)
178+ }
179+
180+ props.value = RootWorkflowProps (childCount = 0 , composeChildren = composeChildren)
181+ testScheduler.runCurrent()
182+ assertEquals(0 , renderings.value.rendering)
183+ }
184+
185+ workflowJob.cancel()
186+ }
187+
188+ private fun benchmarkSimpleTreeSubsequentRender (
189+ composeRoot : Boolean ,
190+ composeChildren : Boolean
191+ ) = runTest {
192+ val props = MutableStateFlow (
193+ RootWorkflowProps (
194+ childCount = MaxChildCount ,
195+ composeChildren = composeChildren
196+ )
197+ )
198+ val workflowJob = Job (parent = coroutineContext.job)
199+ val renderings = renderWorkflowIn(
200+ workflow = if (composeRoot) {
201+ composeSimpleRoot
202+ } else {
203+ traditionalSimpleRoot
204+ },
205+ props = props,
206+ scope = this + workflowJob,
207+ runtimeConfig = RuntimeConfigOptions .ALL ,
208+ onOutput = {}
209+ )
210+
211+ benchmarkRule.measureRepeated {
212+ runWithTimingDisabled {
213+ props.value =
214+ RootWorkflowProps (
215+ childCount = MaxChildCount ,
216+ composeChildren = composeChildren,
217+ childProps = 1
218+ )
219+ testScheduler.runCurrent()
220+ assertEquals(MaxChildCount , renderings.value.rendering)
221+ }
222+
223+ props.value = RootWorkflowProps (
224+ childCount = MaxChildCount ,
225+ composeChildren = composeChildren,
226+ childProps = 2
227+ )
95228 testScheduler.runCurrent()
96- assertEquals(maxChildCount , renderings.value.rendering)
229+ assertEquals(MaxChildCount * 2 , renderings.value.rendering)
97230 }
98231
99232 workflowJob.cancel()
@@ -102,14 +235,16 @@ class ComposeWorkflowMicroBenchmark {
102235
103236private data class RootWorkflowProps (
104237 val childCount : Int ,
105- val composeChildren : Boolean
238+ val composeChildren : Boolean ,
239+ val childProps : Int = 1 ,
106240)
107241
108242private val traditionalSimpleRoot = Workflow .stateless<RootWorkflowProps , Nothing , Int > { props ->
109243 var rendering = 0
110244 repeat(props.childCount) { child ->
111245 rendering + = renderChild(
112246 key = child.toString(),
247+ props = props.childProps,
113248 child = if (props.composeChildren) {
114249 composeSimpleLeaf
115250 } else {
@@ -124,17 +259,16 @@ private val composeSimpleRoot = Workflow.composable<RootWorkflowProps, Nothing,
124259 var rendering = 0
125260 repeat(props.childCount) {
126261 rendering + = renderChild(
262+ props = props.childProps,
127263 workflow = if (props.composeChildren) {
128264 composeSimpleLeaf
129265 } else {
130266 traditionalSimpleLeaf
131267 },
132- props = Unit ,
133- onOutput = null
134268 )
135269 }
136270 rendering
137271}
138272
139- private val traditionalSimpleLeaf = Workflow .stateless<Unit , Nothing , Int > { 1 }
140- private val composeSimpleLeaf = Workflow .composable<Unit , Nothing , Int > { _ , _ -> 1 }
273+ private val traditionalSimpleLeaf = Workflow .stateless<Int , Nothing , Int > { it }
274+ private val composeSimpleLeaf = Workflow .composable<Int , Nothing , Int > { props , _ -> props }
0 commit comments