@@ -4,13 +4,11 @@ package com.squareup.benchmark.composeworkflow.benchmark
4
4
5
5
import androidx.benchmark.junit4.BenchmarkRule
6
6
import androidx.benchmark.junit4.measureRepeated
7
- import androidx.compose.runtime.Composable
8
7
import androidx.test.ext.junit.runners.AndroidJUnit4
9
8
import com.squareup.workflow1.RuntimeConfigOptions
10
9
import com.squareup.workflow1.Workflow
11
10
import com.squareup.workflow1.WorkflowExperimentalApi
12
11
import com.squareup.workflow1.WorkflowExperimentalRuntime
13
- import com.squareup.workflow1.compose.ComposeWorkflow
14
12
import com.squareup.workflow1.compose.composable
15
13
import com.squareup.workflow1.compose.renderChild
16
14
import com.squareup.workflow1.renderChild
@@ -26,6 +24,8 @@ import org.junit.Test
26
24
import org.junit.runner.RunWith
27
25
import kotlin.test.assertEquals
28
26
27
+ private const val MaxChildCount = 100
28
+
29
29
@OptIn(WorkflowExperimentalRuntime ::class )
30
30
@RunWith(AndroidJUnit4 ::class )
31
31
class ComposeWorkflowMicroBenchmark {
@@ -34,38 +34,89 @@ class ComposeWorkflowMicroBenchmark {
34
34
35
35
@Test fun tradRoot_tradChildren_initialRender () {
36
36
benchmarkSimpleTreeInitialRender(
37
- maxChildCount = 100 ,
38
37
composeRoot = false ,
39
38
composeChildren = false
40
39
)
41
40
}
42
41
43
42
@Test fun tradRoot_composeChildren_initialRender () {
44
43
benchmarkSimpleTreeInitialRender(
45
- maxChildCount = 100 ,
46
44
composeRoot = false ,
47
45
composeChildren = true
48
46
)
49
47
}
50
48
51
49
@Test fun composeRoot_tradChildren_initialRender () {
52
50
benchmarkSimpleTreeInitialRender(
53
- maxChildCount = 100 ,
54
51
composeRoot = true ,
55
52
composeChildren = false
56
53
)
57
54
}
58
55
59
56
@Test fun composeRoot_composeChildren_initialRender () {
60
57
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(
62
114
composeRoot = true ,
63
115
composeChildren = true
64
116
)
65
117
}
66
118
67
119
private fun benchmarkSimpleTreeInitialRender (
68
- maxChildCount : Int ,
69
120
composeRoot : Boolean ,
70
121
composeChildren : Boolean
71
122
) = runTest {
@@ -91,9 +142,91 @@ class ComposeWorkflowMicroBenchmark {
91
142
assertEquals(0 , renderings.value.rendering)
92
143
}
93
144
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
+ )
95
228
testScheduler.runCurrent()
96
- assertEquals(maxChildCount , renderings.value.rendering)
229
+ assertEquals(MaxChildCount * 2 , renderings.value.rendering)
97
230
}
98
231
99
232
workflowJob.cancel()
@@ -102,14 +235,16 @@ class ComposeWorkflowMicroBenchmark {
102
235
103
236
private data class RootWorkflowProps (
104
237
val childCount : Int ,
105
- val composeChildren : Boolean
238
+ val composeChildren : Boolean ,
239
+ val childProps : Int = 1 ,
106
240
)
107
241
108
242
private val traditionalSimpleRoot = Workflow .stateless<RootWorkflowProps , Nothing , Int > { props ->
109
243
var rendering = 0
110
244
repeat(props.childCount) { child ->
111
245
rendering + = renderChild(
112
246
key = child.toString(),
247
+ props = props.childProps,
113
248
child = if (props.composeChildren) {
114
249
composeSimpleLeaf
115
250
} else {
@@ -124,17 +259,16 @@ private val composeSimpleRoot = Workflow.composable<RootWorkflowProps, Nothing,
124
259
var rendering = 0
125
260
repeat(props.childCount) {
126
261
rendering + = renderChild(
262
+ props = props.childProps,
127
263
workflow = if (props.composeChildren) {
128
264
composeSimpleLeaf
129
265
} else {
130
266
traditionalSimpleLeaf
131
267
},
132
- props = Unit ,
133
- onOutput = null
134
268
)
135
269
}
136
270
rendering
137
271
}
138
272
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