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
8 changes: 8 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,9 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Date
import java.util.Locale
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 @@ -82,3 +85,8 @@ internal const val ucumUrl = "http://unitsofmeasure.org"

internal fun percentOf(value: Number, total: Number) =
if (total == 0) 0.0 else value.toDouble() / total.toDouble()

/** Implementation of a parallelized map */
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.google.android.fhir.db.impl.dao.ReverseIncludeSearchResult
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.pmap
import com.google.android.fhir.search.SearchQuery
import com.google.android.fhir.toLocalChange
import java.time.Instant
Expand Down Expand Up @@ -205,7 +206,7 @@ internal class DatabaseImpl(
query: SearchQuery,
): List<ResourceWithUUID<R>> {
return db.withTransaction {
resourceDao.getResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray())).map {
resourceDao.getResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray())).pmap {
ResourceWithUUID(it.uuid, iParser.parseResource(it.serializedResource) as R)
}
}
Expand All @@ -217,7 +218,7 @@ internal class DatabaseImpl(
return db.withTransaction {
resourceDao
.getForwardReferencedResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray()))
.map {
.pmap {
ForwardIncludeSearchResult(
it.matchingIndex,
it.baseResourceUUID,
Expand All @@ -233,7 +234,7 @@ internal class DatabaseImpl(
return db.withTransaction {
resourceDao
.getReverseReferencedResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray()))
.map {
.pmap {
ReverseIncludeSearchResult(
it.matchingIndex,
it.baseResourceTypeAndId,
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.pmap {
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