Skip to content

Commit 6c20e7d

Browse files
Merge pull request #1483 from square/sedwards/no-ticket/main-rebase-20260311
Add indexed runtime identity path and hot-path benchmarks
2 parents 9f74e21 + 08e986b commit 6c20e7d

10 files changed

Lines changed: 711 additions & 34 deletions

File tree

benchmarks/runtime-microbenchmark/src/androidTest/kotlin/com/squareup/benchmark/runtime/benchmark/WorkflowRuntimeMicrobenchmark.kt

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.squareup.workflow1.Sink
1111
import com.squareup.workflow1.Snapshot
1212
import com.squareup.workflow1.StatefulWorkflow
1313
import com.squareup.workflow1.StatelessWorkflow
14+
import com.squareup.workflow1.Workflow
1415
import com.squareup.workflow1.WorkflowAction
1516
import com.squareup.workflow1.WorkflowExperimentalRuntime
1617
import com.squareup.workflow1.WorkflowTracer
@@ -54,6 +55,12 @@ class WorkflowRuntimeMicrobenchmark(
5455
private val runtime: BenchmarkRuntimeOptions = NoOptimizations,
5556
) {
5657

58+
private companion object {
59+
const val WideSiblingCount = 250
60+
const val RememberEntryCount = 250
61+
const val StableHandlerCount = 250
62+
}
63+
5764
@get:Rule val benchmarkRule = BenchmarkRule()
5865

5966
@Test fun initialRenderAllChildren() = benchmarkWorkflowPropsChange(
@@ -159,13 +166,92 @@ class WorkflowRuntimeMicrobenchmark(
159166
expectedTestRendering = treeShape.leafCount,
160167
)
161168

169+
@Test fun wideSiblingKeys_initialRenderAllChildren() = benchmarkPropsChange(
170+
workflow = ShallowWideWorkflowRoot(childCount = WideSiblingCount),
171+
setupProps = ShallowWideWorkflowRoot.Props(
172+
renderFirstChild = false,
173+
renderOtherChildren = false,
174+
),
175+
testProps = ShallowWideWorkflowRoot.Props(
176+
renderFirstChild = true,
177+
renderOtherChildren = true,
178+
firstChildProps = 1,
179+
otherChildrenProps = 1,
180+
),
181+
expectedSetupRendering = 0,
182+
expectedTestRendering = WideSiblingCount,
183+
)
184+
185+
@Test fun wideSiblingKeys_rerenderSingleSiblingByPropsChange() = benchmarkPropsChange(
186+
workflow = ShallowWideWorkflowRoot(childCount = WideSiblingCount),
187+
setupProps = ShallowWideWorkflowRoot.Props(
188+
renderFirstChild = true,
189+
renderOtherChildren = true,
190+
firstChildProps = 1,
191+
otherChildrenProps = 1,
192+
),
193+
testProps = ShallowWideWorkflowRoot.Props(
194+
renderFirstChild = true,
195+
renderOtherChildren = true,
196+
firstChildProps = 2,
197+
otherChildrenProps = 1,
198+
),
199+
expectedSetupRendering = WideSiblingCount,
200+
expectedTestRendering = WideSiblingCount + 1,
201+
)
202+
203+
@Test fun rememberManyEntries_sameInputs() = benchmarkPropsChange(
204+
workflow = RememberHeavyWorkflow(entryCount = RememberEntryCount),
205+
setupProps = RememberHeavyWorkflow.Props(baseValue = 1, inputToken = 0),
206+
testProps = RememberHeavyWorkflow.Props(baseValue = 2, inputToken = 0),
207+
expectedSetupRendering = rememberedRendering(baseValue = 1, entryCount = RememberEntryCount),
208+
expectedTestRendering = rememberedRendering(baseValue = 1, entryCount = RememberEntryCount),
209+
)
210+
211+
@Test fun rememberManyEntries_changingInputs() = benchmarkPropsChange(
212+
workflow = RememberHeavyWorkflow(entryCount = RememberEntryCount),
213+
setupProps = RememberHeavyWorkflow.Props(baseValue = 1, inputToken = 0),
214+
testProps = RememberHeavyWorkflow.Props(baseValue = 2, inputToken = 1),
215+
expectedSetupRendering = rememberedRendering(baseValue = 1, entryCount = RememberEntryCount),
216+
expectedTestRendering = rememberedRendering(baseValue = 2, entryCount = RememberEntryCount),
217+
)
218+
219+
@Test fun rememberManyEntries_mixedInputTypes() = benchmarkPropsChange(
220+
workflow = RememberHeavyWorkflow(entryCount = RememberEntryCount, mixedInputTypes = true),
221+
setupProps = RememberHeavyWorkflow.Props(baseValue = 1, inputToken = 0),
222+
testProps = RememberHeavyWorkflow.Props(baseValue = 3, inputToken = 1),
223+
expectedSetupRendering = rememberedRendering(baseValue = 1, entryCount = RememberEntryCount),
224+
expectedTestRendering = rememberedRendering(baseValue = 3, entryCount = RememberEntryCount),
225+
)
226+
227+
@Test fun stableHandlers_manyCallbacks_propChange() = benchmarkPropsChange(
228+
workflow = StableHandlersWorkflow(handlerCount = StableHandlerCount),
229+
setupProps = StableHandlersWorkflow.Props(salt = 0),
230+
testProps = StableHandlersWorkflow.Props(salt = 1),
231+
expectedSetupRendering = StableHandlerCount,
232+
expectedTestRendering = StableHandlerCount + 1,
233+
)
234+
162235
private fun benchmarkWorkflowPropsChange(
163236
setupProps: BenchmarkWorkflowRoot.Props,
164237
testProps: BenchmarkWorkflowRoot.Props,
165238
expectedSetupRendering: Int,
166239
expectedTestRendering: Int,
240+
) = benchmarkPropsChange(
241+
workflow = BenchmarkWorkflowRoot(treeShape = treeShape),
242+
setupProps = setupProps,
243+
testProps = testProps,
244+
expectedSetupRendering = expectedSetupRendering,
245+
expectedTestRendering = expectedTestRendering,
246+
)
247+
248+
private fun <PropsT> benchmarkPropsChange(
249+
workflow: Workflow<PropsT, Nothing, Int>,
250+
setupProps: PropsT,
251+
testProps: PropsT,
252+
expectedSetupRendering: Int,
253+
expectedTestRendering: Int,
167254
) = runTest {
168-
val workflow = BenchmarkWorkflowRoot(treeShape = treeShape)
169255
val props = MutableStateFlow(setupProps)
170256
val workflowJob = Job(parent = coroutineContext.job)
171257
val renderings = renderWorkflowIn(
@@ -454,3 +540,55 @@ private class ShallowWideWorkflowRoot(
454540
override fun snapshotState(state: Int): Snapshot? = null
455541
}
456542
}
543+
544+
private class RememberHeavyWorkflow(
545+
private val entryCount: Int,
546+
private val mixedInputTypes: Boolean = false,
547+
) : StatelessWorkflow<RememberHeavyWorkflow.Props, Nothing, Int>() {
548+
data class Props(
549+
val baseValue: Int,
550+
val inputToken: Int,
551+
)
552+
553+
override fun render(
554+
renderProps: Props,
555+
context: RenderContext<Props, Nothing>
556+
): Int {
557+
var rendering = 0
558+
repeat(entryCount) { index ->
559+
val input = if (mixedInputTypes && index % 2 == 0) {
560+
"token-${renderProps.inputToken}"
561+
} else {
562+
renderProps.inputToken
563+
}
564+
rendering += context.remember("remember-$index", input) {
565+
renderProps.baseValue + index
566+
}
567+
}
568+
return rendering
569+
}
570+
}
571+
572+
private class StableHandlersWorkflow(
573+
private val handlerCount: Int,
574+
) : StatelessWorkflow<StableHandlersWorkflow.Props, Nothing, Int>() {
575+
data class Props(
576+
val salt: Int,
577+
)
578+
579+
override fun render(
580+
renderProps: Props,
581+
context: RenderContext<Props, Nothing>
582+
): Int {
583+
var rendering = renderProps.salt
584+
repeat(handlerCount) { index ->
585+
context.eventHandler<Int>(name = "handler-$index", remember = true) { _ -> }
586+
rendering += 1
587+
}
588+
return rendering
589+
}
590+
}
591+
592+
private fun rememberedRendering(baseValue: Int, entryCount: Int): Int {
593+
return entryCount * baseValue + (entryCount * (entryCount - 1) / 2)
594+
}

0 commit comments

Comments
 (0)