|
| 1 | +# _FHIR Engine Library_ Benchmarks |
| 2 | + |
| 3 | +Benchmarks have been added in the _FHIR Engine Library_ to help track performance regressions |
| 4 | +and areas for performance improvement. |
| 5 | + |
| 6 | +The _FHIR Engine Library_ has the following benchmark modules |
| 7 | + |
| 8 | +1. **app** - configurable android application for testing and running benchmarks |
| 9 | +2. **microbenchmark** - [Jetpack Microbenchmark](https://developer.android.com/topic/performance/benchmarking/microbenchmark-overview) module |
| 10 | +3. **macrobenchmark** - [Jetpack Macrobenchmark](https://developer.android.com/topic/performance/benchmarking/macrobenchmark-overview) module |
| 11 | +to automate testing large-scale user-facing apis with the benchmark app |
| 12 | + |
| 13 | +## App module |
| 14 | + |
| 15 | +Located in the module `:engine:benchmarks:app` |
| 16 | +The _FHIR Engine Library_ Benchmark app runs benchmarks for the _FHIR Engine Library_ APIs including Data Access API, Search API and Sync API. |
| 17 | + |
| 18 | +It can be configured to run the benchmarks for different population sizes whereby population refers to the number of Patients and their associated data |
| 19 | + |
| 20 | +### Configuration |
| 21 | + |
| 22 | +The benchmark app requires that the dataset that is to be benchmarked on be added in the _assets/bulk_data_ folder as `.ndjson` formatted files; whereby each line refers to a single FHIR resource |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | +The dataset can be from an external source. Within the repository, there is a script to generate [synthetic data](https://github.com/synthetichealth/synthea/wiki/Getting-Started) that could then be used for benchmarking. |
| 27 | + |
| 28 | +```shell |
| 29 | +./gradlew :engine:benchmarks:app:generateSynthea -Ppopulation=1000 |
| 30 | +``` |
| 31 | + |
| 32 | +It generates [synthetic data](https://github.com/synthetichealth/synthea/wiki/Getting-Started) with a population size of 1000. |
| 33 | +The `population` parameter determines the population size that would be used to generate the data |
| 34 | + |
| 35 | +### Running |
| 36 | + |
| 37 | +To run this app in Android Studio, [create a run/debug configuration](https://developer.android.com/studio/run/rundebugconfig) for the `:engine:benchmarks:app` module using the [Android App](https://developer.android.com/studio/run/rundebugconfig#android-application) template and run the app using the configuration. |
| 38 | + |
| 39 | +[Change the build variant](https://developer.android.com/studio/run#changing-variant) to `benchmark` for an optimised version of the app, for the best results |
| 40 | + |
| 41 | +Alternatively, run the following command to build and install the benchmark APK on your device/emulator: |
| 42 | + |
| 43 | +```shell |
| 44 | +./gradlew :engine:benchmarks:app:installBenchmark |
| 45 | +``` |
| 46 | + |
| 47 | +## Microbenchmark module |
| 48 | + |
| 49 | +Contains test cases that evaluate the performance of individual tasks executed for the first time directly on hardware, located in the module `:engine:benchmarks:microbenchmark`. |
| 50 | + |
| 51 | +The test cases are designed to run in sequence of their alphabetic order to make sure larger tasks do not build cache for smaller ones. Their class names are prefixed by an extra letter to inform their position relative to others in the list. |
| 52 | + |
| 53 | +### Running |
| 54 | + |
| 55 | +In Android Studio, set your build variants to `release` and run your benchmark as you would any `@Test` using the gutter action next to your test class or method. |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +The results will be similar to this: |
| 60 | +``` |
| 61 | +1,297,374 ns 5345 allocs trace EngineDatabaseBenchmark.createAndGet |
| 62 | +1,114,474,793 ns 4922289 allocs trace FhirSyncWorkerBenchmark.oneTimeSync_50patients |
| 63 | +15,251,125 ns 100542 allocs trace FhirSyncWorkerBenchmark.oneTimeSync_1patient |
| 64 | +179,806,709 ns 986017 allocs trace FhirSyncWorkerBenchmark.oneTimeSync_10patients |
| 65 | +1,451,758 ns 11883 allocs trace GzipUploadInterceptorBenchmark.upload_10patientsWithGzip |
| 66 | +1,537,559 ns 11829 allocs trace GzipUploadInterceptorBenchmark.upload_10patientsWithoutGzip |
| 67 | +73,640,833 ns 1074360 allocs trace GzipUploadInterceptorBenchmark.upload_1000patientsWithGzip |
| 68 | +7,493,642 ns 108428 allocs trace GzipUploadInterceptorBenchmark.upload_100patientsWithoutGzip |
| 69 | +7,799,264 ns 108465 allocs trace GzipUploadInterceptorBenchmark.upload_100patientsWithGzip |
| 70 | +71,189,333 ns 1074466 allocs trace GzipUploadInterceptorBenchmark.upload_1000patientsWithoutGzip |
| 71 | +
|
| 72 | +``` |
| 73 | + |
| 74 | +Alternatively, from the command line, run the connectedCheck to run all of the tests from specified Gradle module: |
| 75 | + |
| 76 | +```bash |
| 77 | +./gradlew :engine:benchmarks:microbenchmark:connectedReleaseAndroidTest |
| 78 | +``` |
| 79 | + |
| 80 | +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 |
| 81 | + |
| 82 | +### Continuous Integration (CI) |
| 83 | + |
| 84 | +#### Configuration |
| 85 | + |
| 86 | +Microbenchmark tests are configured to run in Kokoro and use [Fladle](https://runningcode.github.io/fladle/) plugin, configured through `Project.configureFirebaseTestLabForMicroBenchmark` in file `buildSrc/src/main/kotlin/FirebaseTestLabConfig.kt` |
| 87 | + |
| 88 | +#### Accessing the benchmark results |
| 89 | + |
| 90 | +The Microbenchmark results can be accessed through the following steps |
| 91 | + |
| 92 | +1. Click to `View details` of the `Kokoro: Build and Device Tests` |
| 93 | +  |
| 94 | + |
| 95 | + The details page would look similar to |
| 96 | +  |
| 97 | +2. Within the `Target Log` tab, locate for the section |
| 98 | +  |
| 99 | + with the `TEST FILE NAME` `microbenchmark-release-androidTest.apk` |
| 100 | +3. Select and visit the Google Bucket url that looks as similar to |
| 101 | + [https://console.developers.google.com/storage/browser/android-fhir-build-artifacts/prod/openhealthstack/android-fhir/gcp_ubuntu/presubmit/5404/20250618-172425/firebase/microbenchmark](ttps://console.developers.google.com/storage/browser/android-fhir-build-artifacts/prod/openhealthstack/android-fhir/gcp_ubuntu/presubmit/5404/20250618-172425/firebase/microbenchmark) |
| 102 | + that navigates to the `android-fhir-build-artifacts`  |
| 103 | +4. Navigate to `matrix_0/panther-33-en_US-portrait-test_results_merged.xml` to download the benchmark .xml results file. The `panther-33-en_US-portrait` in the path refers to the Firebase Test Lab device used in running the benchmark tests. |
| 104 | + |
| 105 | +## Macrobenchmark module |
| 106 | + |
| 107 | +The _FHIR Engine Library_ macrobenchmark tests are located in the module `:engine:benchmarks:macrobenchmark` |
| 108 | + |
| 109 | +### Prerequisite |
| 110 | + |
| 111 | +Requires the _FHIR Engine Library_ Benchmark App configured with the relevant benchmark data described in the section for the _FHIR Engine Library_ Benchmark App |
| 112 | + |
| 113 | +### Running |
| 114 | + |
| 115 | +To run, use the command |
| 116 | + |
| 117 | +```shell |
| 118 | +./gradlew :engine:benchmarks:macrobenchmark:connectedCheck |
| 119 | +``` |
| 120 | + |
| 121 | +### Continuous Integration (CI) |
| 122 | + |
| 123 | +#### Configuration |
| 124 | + |
| 125 | +The `FHIR Engine` Macrobenchmarks have been configured to run in Kokoro and use FirebaseTestLab physical devices |
| 126 | + |
| 127 | +Configuration for the Kokoro script are currently located in `kokoro/gcp_ubuntu/kokoro_build.sh` while the FirebaseTestLab testing is configured through the [Fladle](https://runningcode.github.io/fladle/) plugin in `buildSrc/src/main/kotlin/FirebaseTestLabConfig.kt` |
| 128 | + |
| 129 | +#### Accessing the benchmark results |
| 130 | + |
| 131 | +From a GitHub PR , the following steps could be used to download the benchmark results from a Kokoro run |
| 132 | + |
| 133 | +1. Click to `View details` of the `Kokoro: Build and Device Tests` |
| 134 | +  |
| 135 | + |
| 136 | + The details page would look similar to |
| 137 | +  |
| 138 | + |
| 139 | +2. Within the `Target Log` tab, locate for the section |
| 140 | +  |
| 141 | + with the `TEST FILE NAME` `macrobenchmark-benchmark.apk` |
| 142 | + |
| 143 | +3. Select and visit the url as referenced in image |
| 144 | +  |
| 145 | + representative of the Google Cloud Bucket containing the artifacts from the Kokoro run. From the image example, the url is [https://console.developers.google.com/storage/browser/android-fhir-build-artifacts/prod/openhealthstack/android-fhir/gcp_ubuntu/presubmit/5403/20250616-053647/firebase/macrobenchmark](https://console.developers.google.com/storage/browser/android-fhir-build-artifacts/prod/openhealthstack/android-fhir/gcp_ubuntu/presubmit/5403/20250616-053647/firebase/macrobenchmark) |
| 146 | + |
| 147 | + The bucket page would look similar to |
| 148 | +  |
| 149 | + |
| 150 | +4. Navigate to `matrix_0/panther-33-en_US-portrait/artifacts/sdcard/Download/com.google.android.fhir.engine.macrobenchmark-benchmarkData.json` to download the benchmark results file. The `panther-33-en_US-portrait` in the path represents the Firebase Test Lab device that was used to run the benchmark tests. |
| 151 | + |
| 152 | +#### Sample Benchmark Results |
| 153 | + |
| 154 | +The results shared below are generated from running the _FHIR Engine Library_ Macrobenchmark tests in Kokoro |
| 155 | + |
| 156 | +##### [**_Panther - Google Pixel 7_**](https://wiki.lineageos.org/devices/panther/) |
| 157 | + |
| 158 | +**CPU** - Octa-core (2x2.85 GHz Cortex-X1 & 2x2.35 GHz Cortex-A78 & 4x1.80 GHz Cortex-A55) |
| 159 | + |
| 160 | +**RAM** - 8GB |
| 161 | + |
| 162 | +API 33 |
| 163 | + |
| 164 | +###### Data Access API results |
| 165 | + |
| 166 | +Results were generated from execution of FhirEngineCrudBenchmark test in the `engine:benchmarks:macrobenchmark` module located at `engine/benchmarks/macrobenchmark/src/main/java/com/google/android/fhir/engine/macrobenchmark/FhirEngineCrudBenchmark.kt` |
| 167 | + |
| 168 | +| API | Average duration (ms) | Notes | |
| 169 | +|:-------|----------------------:|---------------------------------------| |
| 170 | +| create | ~4.7 | Takes ~47s for population size of 10k | |
| 171 | +| update | ~12.29 | | |
| 172 | +| get | ~3.83 | | |
| 173 | +| delete | ~8.08 | | |
0 commit comments