Skip to content

Commit

Permalink
Update microbenchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
tunjid committed Apr 21, 2024
1 parent 7e056b2 commit 1d383e2
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.example.benchmarks.data

const val PAGE_TO_SCROLL_TO = 10
const val PAGE_TO_SCROLL_TO = 20
const val ITEMS_PER_PAGE = 100

internal const val NUM_PAGES_IN_MEMORY = 3
internal const val MAX_LOAD_SIZE = NUM_PAGES_IN_MEMORY * ITEMS_PER_PAGE

@Suppress("EmptyRange")
val emptyPages = 0 until 0
val onScreenPages = ((PAGE_TO_SCROLL_TO + 1) - NUM_PAGES_IN_MEMORY)..PAGE_TO_SCROLL_TO
val onScreenPages = (PAGE_TO_SCROLL_TO - NUM_PAGES_IN_MEMORY)..PAGE_TO_SCROLL_TO
val offScreenPages = (onScreenPages.first - NUM_PAGES_IN_MEMORY) until onScreenPages.first

sealed interface Benchmarked {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.benchmarks.data

import android.annotation.SuppressLint
import androidx.paging.CombinedLoadStates
import androidx.paging.DifferCallback
import androidx.paging.LoadState
Expand All @@ -12,11 +13,8 @@ import androidx.paging.PagingSource
import androidx.paging.PagingState
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.transformWhile
import kotlinx.coroutines.launch
import kotlin.math.max

private val pagingConfig = PagingConfig(
pageSize = ITEMS_PER_PAGE,
Expand All @@ -30,8 +28,9 @@ class PagingBenchmark(
private val pagesToInvalidate: IntRange
) : Benchmarked {

private var lastInvalidatedPage: Int = Int.MIN_VALUE
private var lastInvalidatedPage: Int = pagesToInvalidate.first + 1

@SuppressLint("RestrictedApi")
override suspend fun benchmark() = coroutineScope {
val differ = pagingDataDiffer()
val collectJob = launch {
Expand All @@ -50,7 +49,6 @@ class PagingBenchmark(
// When items are delivered, read them
.onPagesUpdatedFlow
.transformWhile {
differ.loadStateFlow
if (!differ.loadStateFlow.value.isIdle()) return@transformWhile true
val latestItems = differ.snapshot().items

Expand All @@ -64,29 +62,42 @@ class PagingBenchmark(
return@transformWhile true
}

emit(latestItems)
// Currently at the page scrolled to. If there's nothing to invalidate, complete
if (pagesToInvalidate.isEmpty()) return@transformWhile false

// Find an item from the page that was invalidated
val invalidatedItem = latestItems.lastInvalidatedItem()

val isFinished = invalidatedItem != null
&& invalidatedItem.lastInvalidatedPage >= pagesToInvalidate.last

val isFinished = pagesToInvalidate.isEmpty() ||
lastItem.lastInvalidatedPage >= pagesToInvalidate.last
emit(latestItems)

!isFinished
}
.collect {
// Account for start
lastInvalidatedPage = max(
a = lastInvalidatedPage,
b = pagesToInvalidate.first - 1
)
if (lastInvalidatedPage < pagesToInvalidate.last) {
++lastInvalidatedPage
// Invalidate
val invalidatedItem = it.lastInvalidatedItem()
val canIncrementAndInvalidate = invalidatedItem == null
|| invalidatedItem.lastInvalidatedPage == lastInvalidatedPage

if (canIncrementAndInvalidate && ++lastInvalidatedPage <= pagesToInvalidate.last) {
differ.refresh()
}
}

collectJob.cancel()
}

private fun List<Item>.lastInvalidatedItem(): Item? {
for (item in this) {
if (item.lastInvalidatedPage == lastInvalidatedPage) return item
}
return null
}
}

@SuppressLint("RestrictedApi")
private fun pagingDataDiffer() = object : PagingDataDiffer<Item>(
differCallback = object : DifferCallback {
override fun onChanged(position: Int, count: Int) = Unit
Expand Down Expand Up @@ -151,11 +162,3 @@ private fun LoadStates.isIdle(): Boolean {
prepend is LoadState.NotLoading
}

abstract class M<T> {
abstract val initial: T

private var seed: T = initial

val flow = flowOf(seed)
.onEach { seed = it }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.example.benchmarks.data

import com.tunjid.tiler.PivotRequest
import com.tunjid.tiler.Tile
import com.tunjid.tiler.TiledList
import com.tunjid.tiler.listTiler
import com.tunjid.tiler.queries
import com.tunjid.tiler.toPivotedTileInputs
import com.tunjid.tiler.toTiledList
import kotlinx.coroutines.flow.Flow
Expand All @@ -28,7 +30,7 @@ class TilingBenchmark(
private val pagesToInvalidate: IntRange
) : Benchmarked {

private var lastInvalidatedPage: Int = pagesToInvalidate.first - 1
private var lastInvalidatedPage: Int = pagesToInvalidate.first + 1

override suspend fun benchmark() {
val offsetFlow = MutableSharedFlow<Int>()
Expand All @@ -52,45 +54,48 @@ class TilingBenchmark(
return@transformWhile true
}

// Currently at the page scrolled to. If there's nothing to invalidate, complete
if (pagesToInvalidate.isEmpty()) return@transformWhile false

// ** Invalidation code ** //

// Outside the range, nothing to invalidate so terminate
// Outside the visible range, nothing to invalidate so terminate
if (pagesToInvalidate.first > lastPage) return@transformWhile false
if (pagesToInvalidate.last < firstPage) return@transformWhile false

// Final page invalidated, terminate
val invalidatedPage = latestItems.itemAtPage(lastPage)
println(latestItems.queries())
// Find an item from the page that was invalidated
val invalidatedItem = latestItems.lastInvalidatedItem()

val isFinished = invalidatedPage != null
&& invalidatedPage.lastInvalidatedPage >= pagesToInvalidate.last
val isFinished = invalidatedItem != null
&& invalidatedItem.lastInvalidatedPage >= pagesToInvalidate.last

emit(latestItems)
!isFinished
}
.collect {
// Invalidate
if (++lastInvalidatedPage <= pagesToInvalidate.last) {
val invalidatedItem = it.lastInvalidatedItem()
val canIncrementAndInvalidate = invalidatedItem == null
|| invalidatedItem.lastInvalidatedPage == lastInvalidatedPage

if (canIncrementAndInvalidate && ++lastInvalidatedPage <= pagesToInvalidate.last) {
invalidationSignal.emit(lastInvalidatedPage)
}
}
}
}

private fun List<Item>.itemAtPage(page: Int): Item? {
val startPage = pageFor(first())
val endPage = pageFor(last())
if (page !in startPage..endPage) return null
val diff = page - startPage
val index = diff * ITEMS_PER_PAGE
return get(index)
private fun TiledList<Int, Item>.lastInvalidatedItem(): Item? {
for (item in this) {
if (item.lastInvalidatedPage == lastInvalidatedPage) return item
}
return null
}
}

private fun Flow<Int>.listTiler() = listTiler(
limiter = Tile.Limiter(
maxQueries = NUM_PAGES_IN_MEMORY,
itemSizeHint = ITEMS_PER_PAGE,
),
order = Tile.Order.PivotSorted(
query = 0,
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
androidGradlePlugin = "8.0.2"
androidxActivity = "1.8.2"
androidxAppCompat = "1.6.1"
androidxBenchmark = "1.2.3"
androidxBenchmark = "1.2.4"
androidxCore = "1.12.0"
androidxCompose = "1.6.1"
androidxComposeCompiler = "1.5.8"
Expand Down

0 comments on commit 1d383e2

Please # to comment.