Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Parallelized map and optimize Database search API #2669

Merged
merged 10 commits into from
Oct 7, 2024
9 changes: 9 additions & 0 deletions engine/src/main/java/com/google/android/fhir/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import org.hl7.fhir.r4.model.OperationOutcome
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
Expand Down Expand Up @@ -69,6 +73,11 @@ internal fun Resource.isUploadSuccess(): Boolean {
outcome.issue.all { it.severity.equals(OperationOutcome.IssueSeverity.INFORMATION) }
}

/** Implementation of a parallelized map for CPU intensive tasks */
suspend fun <A, B> Iterable<A>.pmapCPU(f: suspend (A) -> B): List<B> = coroutineScope {
ndegwamartin marked this conversation as resolved.
Show resolved Hide resolved
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}

internal class OffsetDateTimeTypeAdapter : TypeAdapter<OffsetDateTime>() {
override fun write(out: JsonWriter, value: OffsetDateTime) {
out.value(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.annotation.VisibleForTesting
import androidx.room.Room
import androidx.room.withTransaction
import androidx.sqlite.db.SimpleSQLiteQuery
import ca.uhn.fhir.context.FhirContext
import ca.uhn.fhir.parser.IParser
import ca.uhn.fhir.util.FhirTerser
import com.google.android.fhir.DatabaseErrorStrategy
Expand All @@ -37,6 +38,7 @@ import com.google.android.fhir.db.impl.entities.LocalChangeEntity
import com.google.android.fhir.db.impl.entities.ResourceEntity
import com.google.android.fhir.index.ResourceIndexer
import com.google.android.fhir.logicalId
import com.google.android.fhir.pmapCPU
import com.google.android.fhir.search.SearchQuery
import com.google.android.fhir.toLocalChange
import com.google.android.fhir.updateMeta
Expand Down Expand Up @@ -227,8 +229,11 @@ internal class DatabaseImpl(
query: SearchQuery,
): List<ResourceWithUUID<R>> {
return db.withTransaction {
resourceDao.getResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray())).map {
ResourceWithUUID(it.uuid, iParser.parseResource(it.serializedResource) as R)
resourceDao.getResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray())).pmapCPU {
ResourceWithUUID(
it.uuid,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource) as R,
)
}
}
}
Expand All @@ -239,11 +244,12 @@ internal class DatabaseImpl(
return db.withTransaction {
resourceDao
.getForwardReferencedResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray()))
.map {
.pmapCPU {
ForwardIncludeSearchResult(
it.matchingIndex,
it.baseResourceUUID,
iParser.parseResource(it.serializedResource) as Resource,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource)
as Resource,
)
}
}
Expand All @@ -255,11 +261,12 @@ internal class DatabaseImpl(
return db.withTransaction {
resourceDao
.getReverseReferencedResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray()))
.map {
.pmapCPU {
ReverseIncludeSearchResult(
it.matchingIndex,
it.baseResourceTypeAndId,
iParser.parseResource(it.serializedResource) as Resource,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource)
as Resource,
)
}
}
Expand Down
23 changes: 22 additions & 1 deletion engine/src/test/java/com/google/android/fhir/UtilTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022-2023 Google LLC
* Copyright 2022-2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,9 @@ package com.google.android.fhir
import android.os.Build
import com.google.common.truth.Truth.assertThat
import junit.framework.TestCase
import kotlin.system.measureTimeMillis
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.hl7.fhir.r4.model.IdType
import org.hl7.fhir.r4.model.OperationOutcome
import org.hl7.fhir.r4.model.Patient
Expand Down Expand Up @@ -111,6 +114,24 @@ class UtilTest : TestCase() {
assertThat(percentOf(25, 50)).isEqualTo(0.5)
}

@Test
fun `pmap() should execute mapping tasks in parallel`() = runBlocking {
val numberList = listOf(2, 3)
var squaredNumberList: List<Int>

val timeTaken = measureTimeMillis {
squaredNumberList =
numberList.pmapCPU {
delay(1000L)
it * 2
}
}
assertTrue(squaredNumberList.isNotEmpty())
assertThat(squaredNumberList.first()).isEqualTo(4)
assertThat(squaredNumberList.last()).isEqualTo(6)
assertTrue(timeTaken < 2000)
ndegwamartin marked this conversation as resolved.
Show resolved Hide resolved
}

companion object {
val TEST_OPERATION_OUTCOME_ERROR = OperationOutcome()
val TEST_OPERATION_OUTCOME_INFO = OperationOutcome()
Expand Down
Loading