fix(projects): parallelize project indexing with coroutines

This commit is contained in:
Akash Yadav
2023-10-02 15:05:21 +05:30
parent 1d54a3ff4d
commit 66aa9099b4
6 changed files with 75 additions and 24 deletions
+1 -1
View File
@@ -90,7 +90,7 @@ dependencies {
implementation(libs.common.utilcode)
implementation(libs.common.glide)
implementation(libs.common.jsoup)
implementation(libs.common.kotlin.coroutines)
implementation(libs.common.kotlin.coroutines.android)
implementation(libs.google.auto.service.annotations)
implementation(libs.google.gson)
+1 -1
View File
@@ -39,7 +39,7 @@ common-javaparser = { module = "com.github.javaparser:javaparser-symbol-solver-c
common-lang3 = { module = "org.apache.commons:commons-lang3", version = "3.13.0" }
common-io = { module = "commons-io:commons-io", version = "2.13.0" }
common-kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
common-kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlin-coroutines" }
common-kotlin-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlin-coroutines" }
common-jkotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
common-eventbus_ap = { module = "org.greenrobot:eventbus-annotation-processor", version = "3.3.1" }
common-javapoet = { module = "com.squareup:javapoet", version = "1.13.0" }
@@ -48,3 +48,21 @@ constructor(
lastLap = System.currentTimeMillis()
}
}
/**
* Run the given action with a stopwatch to log the time the action took to execute.
*
* @see StopWatch
*/
inline fun <R> withStopWatch(
label: String,
start: Long = System.currentTimeMillis(),
lastLap: Long = start,
action: (StopWatch) -> R
): R {
return StopWatch(label, start, lastLap).run {
action(this).also {
log()
}
}
}
+1
View File
@@ -32,6 +32,7 @@ dependencies {
implementation(projects.subprojects.xmlUtils)
implementation(libs.common.io)
implementation(libs.common.kotlin.coroutines.android)
implementation(libs.google.auto.service.annotations)
implementation(libs.google.guava)
@@ -42,6 +42,15 @@ import com.itsaky.androidide.tooling.api.models.BuildVariantInfo
import com.itsaky.androidide.utils.DocumentUtils
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.withStopWatch
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@@ -88,20 +97,32 @@ class ProjectManagerImpl : IProjectManager, EventReceiver {
override fun setupProject(project: IProject) {
this.rootProject = ProjectTransformer().transform(CachingProject(project))
if (this.rootProject == null) {
return
}
val rootProject = this.rootProject ?: return
// build variants must be updated before the sources and classpaths are indexed
updateBuildVariants { buildVariants ->
androidBuildVariants = buildVariants
}
this.rootProject!!.subProjects.filterIsInstance(ModuleProject::class.java).forEach {
it.indexSourcesAndClasspaths()
if (it is AndroidModule) {
it.readResources()
withStopWatch("Setup project") {
val indexerScope = CoroutineScope(Dispatchers.Default)
val modulesFlow = flow {
rootProject.subProjects.filterIsInstance(ModuleProject::class.java).forEach {
emit(it)
}
}
val jobs = modulesFlow.map { module ->
indexerScope.async {
module.indexSourcesAndClasspaths()
if (module is AndroidModule) {
module.readResources()
}
}
}
// wait for the indexing to finish
runBlocking { jobs.toList().awaitAll() }
}
}
@@ -37,11 +37,20 @@ import com.itsaky.androidide.tooling.api.models.BasicAndroidVariantMetadata
import com.itsaky.androidide.tooling.api.models.GradleTask
import com.itsaky.androidide.tooling.api.util.findPackageName
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.withStopWatch
import com.itsaky.androidide.xml.resources.ResourceTableRegistry
import com.itsaky.androidide.xml.versions.ApiVersions
import com.itsaky.androidide.xml.versions.ApiVersionsRegistry
import com.itsaky.androidide.xml.widgets.WidgetTable
import com.itsaky.androidide.xml.widgets.WidgetTableRegistry
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import java.io.File
import java.util.concurrent.CompletableFuture
@@ -244,25 +253,27 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
* Reads the resource files are creates the [com.android.aaptcompiler.ResourceTable] instances for
* the corresponding resource directories.
*/
fun readResources() {
suspend fun readResources() {
// Read resources in parallel
val threads = mutableListOf<Thread>()
threads.add(
Thread {
getFrameworkResourceTable()
getResourceTable()
getDependencyResourceTables()
withStopWatch("Read resources for module : $path") {
val resourceReaderScope = CoroutineScope(
Dispatchers.IO + CoroutineName("ResourceReader($path)"))
val resourceFlow = flow {
emit(getFrameworkResourceTable())
emit(getResourceTable())
emit(getDependencyResourceTables())
emit(getApiVersions())
emit(getWidgetTable())
}
)
threads.add(Thread(this::getApiVersions))
threads.add(Thread(this::getWidgetTable))
for (thread in threads) {
thread.start()
}
val jobs = resourceFlow.map { result ->
resourceReaderScope.async {
result
}
}
for (thread in threads) {
thread.join()
jobs.toList().awaitAll()
}
}