Skip to content

Commit 553a902

Browse files
committed
Demo fhirEngine benchmark to test with synthea data
1 parent 37bac7d commit 553a902

File tree

10 files changed

+110
-8
lines changed

10 files changed

+110
-8
lines changed

buildSrc/src/main/kotlin/Plugins.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ object Plugins {
4444

4545
object Versions {
4646
const val androidGradlePlugin = "8.5.0"
47-
const val benchmarkPlugin = "1.1.0"
47+
const val benchmarkPlugin = "1.3.4"
4848
const val dokka = "1.9.20"
4949
const val kspPlugin = "1.9.22-1.0.18"
5050
}

buildSrc/src/main/kotlin/Sdk.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
object Sdk {
18-
const val COMPILE_SDK = 33
18+
const val COMPILE_SDK = 36
1919
const val TARGET_SDK = 31
2020

2121
// Engine and SDC must support API 24.

engine/.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
/build
2-
/synthea

engine/benchmark/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
/build
2+
/synthea
3+
/src/androidTest/assets/bulk_data

engine/benchmark/README.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,16 @@ Alternatively, from the command line, run the connectedCheck to run all of the t
3131
./gradlew :engine:benchmark:connectedReleaseAndroidTest
3232
```
3333

34-
In this case, results will be saved to the `outputs/androidTest-results/connected/<device>/test-result.pb`. To visualize on Android Studio, click Run / Import Tests From File and find the `.pb` file
34+
In this case, results will be saved to the `outputs/androidTest-results/connected/<device>/test-result.pb`. To visualize on Android Studio, click Run / Import Tests From File and find the `.pb` file
35+
36+
# How to run the benchmark with Synthea for different population sizes
37+
1. Change into the project's root directory
38+
2. Run script to generate population data `sh engine/benchmark/generate_synthea.sh [<population-size>]`
39+
```bash
40+
# For population of 100 patients
41+
sh engine/benchmark/generate_synthea.sh 100
42+
```
43+
3. Run benchmarks
44+
```bash
45+
./gradlew :engine:benchmark:connectedReleaseAndroidTest
46+
```

engine/benchmark/build.gradle.kts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ android {
1010
defaultConfig {
1111
minSdk = Sdk.MIN_SDK
1212
testInstrumentationRunner = Dependencies.androidBenchmarkRunner
13+
// Enable measuring on an emulator, or devices with low battery
14+
// testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR,LOW-BATTERY"
1315
}
1416

1517
testBuildType = "release"

engine/generate_synthea.sh renamed to engine/benchmark/generate_synthea.sh

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
POPULATION="${1:-3}"
55
SCRIPT_DIR=$(dirname "$0")
66
SYNTHEA_DIR="$SCRIPT_DIR"/synthea
7+
GIT_ROOT_DIR=$(git rev-parse --show-toplevel)
78

89
if [ ! -d "$SYNTHEA_DIR" ]; then
910
# build synthea according to https://github.com/synthetichealth/synthea
@@ -15,5 +16,7 @@ pushd "$SYNTHEA_DIR" || exit
1516
popd || exit
1617

1718
# Move to benchmark assets dir
18-
mkdir -p "$SCRIPT_DIR"/benchmark/src/androidTest/assets/bulk_data
19-
cp "$SYNTHEA_DIR"/output/fhir/Patient*.ndjson "$SCRIPT_DIR"/benchmark/src/androidTest/assets/bulk_data
19+
mkdir -p "$GIT_ROOT_DIR"/engine/benchmark/src/androidTest/assets/bulk_data
20+
cp "$SYNTHEA_DIR"/output/fhir/Patient*.ndjson "$GIT_ROOT_DIR"/engine/benchmark/src/androidTest/assets/bulk_data
21+
22+
rm -rf "$SYNTHEA_DIR"/output
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.android.fhir.benchmark
18+
19+
import android.content.Context
20+
import androidx.benchmark.junit4.BenchmarkRule
21+
import androidx.benchmark.junit4.measureRepeated
22+
import androidx.test.core.app.ApplicationProvider
23+
import androidx.test.ext.junit.runners.AndroidJUnit4
24+
import androidx.test.filters.LargeTest
25+
import ca.uhn.fhir.context.FhirContext
26+
import ca.uhn.fhir.context.FhirVersionEnum
27+
import com.google.android.fhir.FhirEngineConfiguration
28+
import com.google.android.fhir.FhirEngineProvider
29+
import com.google.android.fhir.search.count
30+
import com.google.common.truth.Truth.assertThat
31+
import kotlinx.coroutines.runBlocking
32+
import org.hl7.fhir.r4.model.Patient
33+
import org.hl7.fhir.r4.model.Resource
34+
import org.junit.AfterClass
35+
import org.junit.BeforeClass
36+
import org.junit.Rule
37+
import org.junit.Test
38+
import org.junit.runner.RunWith
39+
40+
@LargeTest
41+
@RunWith(AndroidJUnit4::class)
42+
class DemoFhirEngineBenchmark {
43+
44+
@get:Rule val benchmarkRule = BenchmarkRule()
45+
private val applicationContext = ApplicationProvider.getApplicationContext<Context>()
46+
private val assetManager = applicationContext.assets
47+
private val fhirContext = FhirContext.forCached(FhirVersionEnum.R4)
48+
49+
@Test
50+
fun create() {
51+
val bulkFiles =
52+
assetManager.list(BULK_DATA_DIR)?.filter { it.endsWith(".ndjson") } ?: emptyList()
53+
val resources =
54+
bulkFiles
55+
.asSequence()
56+
.map { assetManager.open("$BULK_DATA_DIR/$it") }
57+
.flatMap { inputStream -> inputStream.bufferedReader().readLines() }
58+
.map { fhirContext.newJsonParser().parseResource(it) as Resource }
59+
.toList()
60+
61+
val fhirEngine = FhirEngineProvider.getInstance(applicationContext)
62+
63+
benchmarkRule.measureRepeated { runBlocking { fhirEngine.create(*resources.toTypedArray()) } }
64+
assertThat(runBlocking { fhirEngine.count<Patient> {} }).isGreaterThan(1L)
65+
}
66+
67+
companion object {
68+
private const val BULK_DATA_DIR = "bulk_data"
69+
70+
@JvmStatic
71+
@BeforeClass
72+
fun oneTimeSetup() {
73+
FhirEngineProvider.init(FhirEngineConfiguration(testMode = true))
74+
}
75+
76+
@JvmStatic
77+
@AfterClass
78+
fun oneTimeTearDown() {
79+
FhirEngineProvider.cleanup()
80+
}
81+
}
82+
}

engine/benchmark/src/androidTest/java/com/google/android/fhir/benchmark/FhirSyncWorkerBenchmark.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 Google LLC
2+
* Copyright 2023-2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import androidx.benchmark.junit4.BenchmarkRule
2121
import androidx.benchmark.junit4.measureRepeated
2222
import androidx.test.core.app.ApplicationProvider
2323
import androidx.test.ext.junit.runners.AndroidJUnit4
24+
import androidx.test.filters.LargeTest
2425
import androidx.test.filters.SdkSuppress
2526
import androidx.work.Data
2627
import androidx.work.ListenableWorker
@@ -73,6 +74,7 @@ import org.junit.Rule
7374
import org.junit.Test
7475
import org.junit.runner.RunWith
7576

77+
@LargeTest
7678
@RunWith(AndroidJUnit4::class)
7779
class FhirSyncWorkerBenchmark {
7880

gradle/libs.versions.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ android-fhir-knowledge = "0.1.0-beta01"
77
androidx-activity = "1.7.2"
88
androidx-appcompat = "1.6.1"
99
androidx-arch-core = "2.2.0"
10-
androidx-benchmark = "1.1.1"
10+
androidx-benchmark = "1.3.4"
1111
androidx-constraintlayout = "2.1.4"
1212
androidx-core = "1.10.1"
1313
androidx-datastore = "1.0.0"

0 commit comments

Comments
 (0)