-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added shared prefs edit benchmark (commit vs apply)
- Loading branch information
1 parent
bf3823e
commit becc656
Showing
13 changed files
with
502 additions
and
0 deletions.
There are no files selected for viewing
115 changes: 115 additions & 0 deletions
115
.../techyourchance/android/benchmarks/shared_prefs_write/SharedPrefsWriteBenchmarkUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package com.techyourchance.android.benchmarks.shared_prefs_write | ||
|
||
import android.content.Context | ||
import android.content.SharedPreferences | ||
import com.techyourchance.android.common.datetime.DateTimeProvider | ||
import com.techyourchance.android.common.maths.LinearFitCalculator | ||
import kotlinx.coroutines.asCoroutineDispatcher | ||
import kotlinx.coroutines.withContext | ||
import java.util.concurrent.Executors | ||
import javax.inject.Inject | ||
|
||
class SharedPrefsWriteBenchmarkUseCase @Inject constructor( | ||
private val context: Context, | ||
private val dateTimeProvider: DateTimeProvider, | ||
private val linearFitCalculator: LinearFitCalculator, | ||
) { | ||
|
||
data class Result( | ||
val resultWithCommit: SharedPrefsWriteResult, | ||
val resultWithApply: SharedPrefsWriteResult, | ||
) | ||
|
||
private val coroutinesDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() | ||
|
||
suspend fun runBenchmark(valueToWrite: String): Result { | ||
return withContext(coroutinesDispatcher) { | ||
|
||
val sharedPrefs = context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) | ||
|
||
val commitRawTimingsNano = runBenchmarkWithWriteFunction(sharedPrefs, valueToWrite) { | ||
it.commit() | ||
} | ||
|
||
val applyRawTimingsNano = runBenchmarkWithWriteFunction(sharedPrefs, valueToWrite) { | ||
it.apply() | ||
} | ||
|
||
val resultWithCommit = computeResult(commitRawTimingsNano) | ||
|
||
val resultWithApply = computeResult(applyRawTimingsNano) | ||
|
||
Result(resultWithCommit, resultWithApply) | ||
} | ||
} | ||
|
||
private fun runBenchmarkWithWriteFunction( | ||
sharedPrefs: SharedPreferences, | ||
sharedPrefValue: String, | ||
editorWriteFunction: (SharedPreferences.Editor) -> Unit | ||
): MutableMap<Int, MutableMap<Int, Long>> { | ||
|
||
var sharedPrefsEditor: SharedPreferences.Editor? = null | ||
var sharedPrefKey = "" | ||
var startNano = 0L | ||
var endNano = 0L | ||
|
||
val rawTimingsNano = mutableMapOf<Int, MutableMap<Int, Long>>() | ||
|
||
for (benchmarkIteration in 0 until NUM_ITERATIONS) { | ||
sharedPrefs.edit().clear().commit() | ||
|
||
rawTimingsNano[benchmarkIteration] = mutableMapOf() | ||
for (prefEntryIndex in 0 until NUM_OF_WRITES_PER_ITERATION) { | ||
|
||
sharedPrefKey = | ||
PREF_KEY_PREFIX + prefEntryIndex.toString().padEnd(NUM_OF_WRITES_PER_ITERATION.toString().length) | ||
|
||
startNano = dateTimeProvider.getNanoTime() | ||
sharedPrefsEditor = sharedPrefs.edit().putString(sharedPrefKey, sharedPrefValue) | ||
editorWriteFunction(sharedPrefsEditor) | ||
endNano = dateTimeProvider.getNanoTime() | ||
|
||
rawTimingsNano[benchmarkIteration]!![prefEntryIndex] = endNano - startNano | ||
|
||
} | ||
} | ||
return rawTimingsNano | ||
} | ||
|
||
|
||
private fun computeResult(rawTimingsNano: Map<Int, MutableMap<Int, Long>>): SharedPrefsWriteResult { | ||
val averageWriteDurationsWithCommit = computeAverageWriteDurationsNano(rawTimingsNano) | ||
return SharedPrefsWriteResult( | ||
averageWriteDurationsWithCommit, | ||
linearFitCalculator.calculateLinearFit( | ||
averageWriteDurationsWithCommit.map { entry -> Pair(entry.key.toDouble(), entry.value.toDouble()) } | ||
) | ||
) | ||
} | ||
|
||
private fun computeAverageWriteDurationsNano(sharedPrefsWriteTimings: Map<Int, Map<Int, Long>>): Map<Int, Long> { | ||
val sumsOfWriteTimes = computeSumsWithInternalKeys(sharedPrefsWriteTimings) | ||
return sumsOfWriteTimes.mapValues { entry -> | ||
(entry.value.toDouble() / sharedPrefsWriteTimings.size).toLong() | ||
} | ||
} | ||
|
||
private fun computeSumsWithInternalKeys(externalMap: Map<Int, Map<Int, Long>>): Map<Int, Long> { | ||
val sums = mutableMapOf<Int, Long>() | ||
for ((externalKey, internalMap) in externalMap) { | ||
for ((internalKey, value) in internalMap) { | ||
sums[internalKey] = sums.getOrDefault(internalKey, 0) + value | ||
} | ||
} | ||
return sums | ||
} | ||
|
||
companion object { | ||
|
||
private const val SHARED_PREFS_NAME = "benchmark_shared_prefs" | ||
private const val NUM_OF_WRITES_PER_ITERATION = 100 | ||
private const val PREF_KEY_PREFIX = "key" | ||
private const val NUM_ITERATIONS = 10 | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
...n/java/com/techyourchance/android/benchmarks/shared_prefs_write/SharedPrefsWriteResult.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.techyourchance.android.benchmarks.shared_prefs_write | ||
|
||
import com.techyourchance.android.common.maths.LinearFitCoefficients | ||
|
||
data class SharedPrefsWriteResult( | ||
val entryIndexToAverageEditDurationsNano: Map<Int, Long>, | ||
val linearFitCoefficients: LinearFitCoefficients, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
app/src/main/java/com/techyourchance/android/common/random/RandomStringsGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.techyourchance.android.common.random | ||
|
||
import javax.inject.Inject | ||
import java.lang.StringBuilder | ||
import java.security.SecureRandom | ||
import java.util.concurrent.locks.ReentrantLock | ||
import kotlin.concurrent.withLock | ||
|
||
class RandomStringsGenerator @Inject constructor() { | ||
|
||
fun getRandomAlphanumericString(length: Int): String { | ||
return getRandomStringFromAlphabet(ALPHANUMERIC, length) | ||
} | ||
|
||
fun getRandomNumericString(length: Int): String { | ||
return getRandomStringFromAlphabet(NUMERIC, length) | ||
} | ||
|
||
private fun getRandomStringFromAlphabet(alphabet: String, length: Int): String { | ||
val sb = StringBuilder(length) | ||
for (i in 0 until length) { | ||
var position: Int | ||
Companion.LOCK.withLock { // thread-safe protection of SecureRandom (just in case) | ||
position = SECURE_RANDOM.nextInt(alphabet.length) | ||
} | ||
sb.append(alphabet[position]) | ||
} | ||
return sb.toString() | ||
} | ||
|
||
companion object { | ||
private val SECURE_RANDOM = SecureRandom() | ||
private val LOCK = ReentrantLock() | ||
|
||
private const val ALPHANUMERIC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | ||
private const val NUMERIC = "0123456789" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
...com/techyourchance/android/screens/benchmarks/sharedprefs/SharedPrefsBenchmarkFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package com.techyourchance.android.screens.benchmarks.sharedprefs | ||
|
||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import com.techyourchance.android.benchmarks.shared_prefs_write.SharedPrefsWriteBenchmarkUseCase | ||
import com.techyourchance.android.common.random.RandomStringsGenerator | ||
import com.techyourchance.android.screens.common.ScreensNavigator | ||
import com.techyourchance.android.screens.common.dialogs.DialogsNavigator | ||
import com.techyourchance.android.screens.common.fragments.BaseFragment | ||
import com.techyourchance.android.screens.common.mvcviews.ViewMvcFactory | ||
import kotlinx.coroutines.Job | ||
import kotlinx.coroutines.launch | ||
import javax.inject.Inject | ||
|
||
class SharedPrefsBenchmarkFragment : BaseFragment(), SharedPrefsBenchmarkViewMvc.Listener { | ||
|
||
@Inject lateinit var viewMvcFactory: ViewMvcFactory | ||
@Inject lateinit var dialogsNavigator: DialogsNavigator | ||
@Inject lateinit var screensNavigator: ScreensNavigator | ||
@Inject lateinit var sharedPrefsWriteBenchmarkUseCase: SharedPrefsWriteBenchmarkUseCase | ||
@Inject lateinit var randomStringsGenerator: RandomStringsGenerator | ||
|
||
private lateinit var viewMvc: SharedPrefsBenchmarkViewMvc | ||
|
||
private var startBenchmark = false | ||
|
||
private var benchmarkJob: Job? = null | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
controllerComponent.inject(this) | ||
super.onCreate(savedInstanceState) | ||
} | ||
|
||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { | ||
viewMvc = viewMvcFactory.newSharedPrefsBenchmarkViewMvc(container) | ||
return viewMvc.getRootView() | ||
} | ||
|
||
override fun onStart() { | ||
super.onStart() | ||
viewMvc.registerListener(this) | ||
viewMvc.showBenchmarkStopped() | ||
if (startBenchmark) { | ||
startBenchmark() | ||
} | ||
} | ||
|
||
override fun onStop() { | ||
super.onStop() | ||
viewMvc.unregisterListener(this) | ||
benchmarkJob?.cancel() | ||
} | ||
|
||
override fun onToggleBenchmarkClicked() { | ||
benchmarkJob?.let { | ||
if (it.isActive) { | ||
it.cancel() | ||
} else { | ||
startBenchmark() | ||
} | ||
} ?: startBenchmark() | ||
} | ||
|
||
private fun startBenchmark() { | ||
benchmarkJob = coroutineScope.launch { | ||
try { | ||
viewMvc.showBenchmarkStarted() | ||
val valueToWrite = randomStringsGenerator.getRandomAlphanumericString(PREF_VALUE_LENGTH) | ||
val result = sharedPrefsWriteBenchmarkUseCase.runBenchmark(valueToWrite) | ||
viewMvc.bindBenchmarkResults( | ||
PREF_VALUE_LENGTH, | ||
result.resultWithCommit, | ||
result.resultWithApply | ||
) | ||
} finally { | ||
viewMvc.showBenchmarkStopped() | ||
} | ||
} | ||
} | ||
|
||
override fun onBackClicked() { | ||
screensNavigator.navigateBack() | ||
} | ||
|
||
companion object { | ||
|
||
private const val PREF_VALUE_LENGTH = 100 | ||
|
||
fun newInstance(): SharedPrefsBenchmarkFragment { | ||
val args = Bundle(3) | ||
val fragment = SharedPrefsBenchmarkFragment() | ||
fragment.arguments = args | ||
return fragment | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
.../com/techyourchance/android/screens/benchmarks/sharedprefs/SharedPrefsBenchmarkViewMvc.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.techyourchance.android.screens.benchmarks.sharedprefs | ||
|
||
import com.techyourchance.android.benchmarks.shared_prefs_write.SharedPrefsWriteResult | ||
import com.techyourchance.android.screens.common.mvcviews.BaseObservableViewMvc | ||
|
||
abstract class SharedPrefsBenchmarkViewMvc: BaseObservableViewMvc<SharedPrefsBenchmarkViewMvc.Listener>() { | ||
|
||
interface Listener { | ||
fun onBackClicked() | ||
fun onToggleBenchmarkClicked() | ||
} | ||
|
||
abstract fun bindBenchmarkResults( | ||
prefValueLength: Int, | ||
resultWithCommit: SharedPrefsWriteResult, | ||
resultWithApply: SharedPrefsWriteResult, | ||
) | ||
abstract fun showBenchmarkStarted() | ||
abstract fun showBenchmarkStopped() | ||
} |
Oops, something went wrong.