├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── LICENSE ├── README.md ├── android-perf-screenshot.png ├── app ├── build.gradle.kts ├── objectbox-models │ └── default.json ├── proguard-rules.pro ├── schemas │ └── io.objectbox.performanceapp.room.AppDatabase │ │ └── 1.json └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── io │ │ └── objectbox │ │ └── performanceapp │ │ ├── Benchmark.java │ │ ├── MainActivity.java │ │ ├── PerfTest.java │ │ ├── PerfTestRunner.java │ │ ├── RandomValues.java │ │ ├── TestType.java │ │ ├── greendao │ │ ├── DaoMaster.java │ │ ├── DaoSession.java │ │ ├── GreendaoPerfTest.java │ │ ├── SimpleEntity.java │ │ ├── SimpleEntityDao.java │ │ ├── SimpleEntityIndexed.java │ │ └── SimpleEntityIndexedDao.java │ │ ├── objectbox │ │ ├── ObjectBoxPerfTest.java │ │ ├── SimpleEntity.java │ │ └── SimpleEntityIndexed.java │ │ ├── realm │ │ ├── RealmPerfTest.java │ │ ├── SimpleEntity.java │ │ └── SimpleEntityIndexed.java │ │ └── room │ │ ├── AppDatabase.java │ │ ├── RoomPerfTest.java │ │ ├── SimpleEntity.java │ │ ├── SimpleEntityDao.java │ │ ├── SimpleEntityIndexed.java │ │ └── SimpleEntityIndexedDao.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── greenDAO-generator ├── build.gradle └── src │ └── io │ └── objectbox │ └── performanceapp │ └── generator │ └── TestDaoGenerator.java └── settings.gradle /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Java CI with Gradle 5 | 6 | on: 7 | push: 8 | pull_request: 9 | workflow_dispatch: # Allow running manually from web UI 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Check out 18 | uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 19 | 20 | - name: Set up JDK 17 21 | uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 22 | with: 23 | distribution: 'temurin' 24 | java-version: '17' 25 | cache: gradle 26 | 27 | - name: Gradle Info 28 | run: ./gradlew -version 29 | 30 | - name: Build with Gradle 31 | run: ./gradlew build 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | build/ 7 | /captures 8 | .externalNativeBuild 9 | default.json.bak -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ObjectBox Java Database Performance Benchmarks 2 | 3 | This is an Android app to measure object persistence performance of 4 | - [ObjectBox](/app/src/main/java/io/objectbox/performanceapp/objectbox) 5 | - [Realm](/app/src/main/java/io/objectbox/performanceapp/realm) 6 | - [SQLite using Room](/app/src/main/java/io/objectbox/performanceapp/room) 7 | - [SQLite using greenDAO](/app/src/main/java/io/objectbox/performanceapp/greendao) (deprecated) 8 | 9 | Results are printed on the UI and saved as tab-separated files (`.tvs`) that can be easily imported 10 | into a spreadsheet. The files are located on external storage. 11 | 12 | 13 | 14 | ## How to get good results 15 | 16 | * Tests perform differently when multiple databases are selected: 17 | For comparable results, run only a single database at a time. 18 | * Put the test device into air plane mode to avoid background apps doing sync over the network. 19 | * Screen must be on at all times (e.g. plug the device in). 20 | * Beware of lazy loaded data (e.g. properties on live objects of Realm): 21 | loading objects may seem very fast because no data is actually loaded. 22 | For better comparison it may be necessary to access data (at least once) and combine load and access time to get actual read time. 23 | * We also have written some general notes on [benchmarking on Android](https://greenrobot.org/android/benchmarking-on-android/). -------------------------------------------------------------------------------- /android-perf-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objectbox/objectbox-performance/e8d3c35b2ab4fc774c04284489b7c275499f5470/android-perf-screenshot.png -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("io.objectbox") 4 | id("realm-android") 5 | } 6 | 7 | android { 8 | namespace = "io.objectbox.performanceapp" 9 | compileSdk = 34 // Android 14 10 | 11 | buildFeatures { 12 | viewBinding = true 13 | } 14 | 15 | compileOptions { 16 | sourceCompatibility = JavaVersion.VERSION_1_8 17 | targetCompatibility = JavaVersion.VERSION_1_8 18 | } 19 | 20 | defaultConfig { 21 | applicationId = "io.objectbox.performanceapp" 22 | minSdk = 19 // Android 4.4 23 | targetSdk = 34 // Android 14 24 | versionCode = 1 25 | versionName = "1.0" 26 | 27 | javaCompileOptions { 28 | annotationProcessorOptions { 29 | arguments(mapOf("room.schemaLocation" to "$projectDir/schemas")) 30 | } 31 | } 32 | } 33 | 34 | buildTypes { 35 | getByName("release") { 36 | isMinifyEnabled = false 37 | proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") 38 | signingConfig = signingConfigs.getByName("debug") 39 | } 40 | create("releaseDebugCert") { 41 | initWith(getByName("release")) 42 | // Just to use without checkjni 43 | signingConfig = signingConfigs.getByName("debug") 44 | } 45 | create("debugJniNoDebug") { 46 | initWith(getByName("debug")) 47 | // Just to use without checkjni 48 | isJniDebuggable = false 49 | } 50 | } 51 | } 52 | 53 | // Print deprecation warnings like Kotlin 54 | tasks.withType(JavaCompile::class).configureEach { 55 | options.isDeprecation = true 56 | } 57 | 58 | dependencies { 59 | implementation("androidx.preference:preference:1.2.1") 60 | implementation("androidx.room:room-runtime:2.6.1") 61 | annotationProcessor("androidx.room:room-compiler:2.6.1") 62 | implementation("org.greenrobot:greendao:3.3.0") 63 | implementation("org.greenrobot:essentials:3.1.0") 64 | } 65 | -------------------------------------------------------------------------------- /app/objectbox-models/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", 3 | "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", 4 | "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", 5 | "entities": [ 6 | { 7 | "id": "1:2066508037409008861", 8 | "lastPropertyId": "10:8979168811650087844", 9 | "name": "SimpleEntity", 10 | "properties": [ 11 | { 12 | "id": "1:8357181620038296370", 13 | "name": "id", 14 | "type": 6, 15 | "flags": 1 16 | }, 17 | { 18 | "id": "2:8014957599658721377", 19 | "name": "simpleBoolean", 20 | "type": 1 21 | }, 22 | { 23 | "id": "3:486615229902264705", 24 | "name": "simpleByte", 25 | "type": 2 26 | }, 27 | { 28 | "id": "4:6601224481371519086", 29 | "name": "simpleShort", 30 | "type": 3 31 | }, 32 | { 33 | "id": "5:2118306784392599510", 34 | "name": "simpleInt", 35 | "type": 5 36 | }, 37 | { 38 | "id": "6:5812772956445341632", 39 | "name": "simpleLong", 40 | "type": 6 41 | }, 42 | { 43 | "id": "7:1065418842978193908", 44 | "name": "simpleFloat", 45 | "type": 7 46 | }, 47 | { 48 | "id": "8:7220777031930057031", 49 | "name": "simpleDouble", 50 | "type": 8 51 | }, 52 | { 53 | "id": "9:6669079511137612739", 54 | "name": "simpleString", 55 | "type": 9 56 | }, 57 | { 58 | "id": "10:8979168811650087844", 59 | "name": "simpleByteArray", 60 | "type": 23 61 | } 62 | ], 63 | "relations": [] 64 | }, 65 | { 66 | "id": "2:1960446593108937414", 67 | "lastPropertyId": "10:7622112042154959206", 68 | "name": "SimpleEntityIndexed", 69 | "properties": [ 70 | { 71 | "id": "1:3687894186713655139", 72 | "name": "id", 73 | "type": 6, 74 | "flags": 1 75 | }, 76 | { 77 | "id": "2:744491936175748739", 78 | "name": "simpleBoolean", 79 | "type": 1 80 | }, 81 | { 82 | "id": "3:930101374761292306", 83 | "name": "simpleByte", 84 | "type": 2 85 | }, 86 | { 87 | "id": "4:5019932040401094666", 88 | "name": "simpleShort", 89 | "type": 3 90 | }, 91 | { 92 | "id": "5:9036275660773544260", 93 | "name": "simpleInt", 94 | "indexId": "1:7769255088226738365", 95 | "type": 5, 96 | "flags": 8 97 | }, 98 | { 99 | "id": "6:5133250005110876084", 100 | "name": "simpleLong", 101 | "type": 6 102 | }, 103 | { 104 | "id": "7:7794332651287140849", 105 | "name": "simpleFloat", 106 | "type": 7 107 | }, 108 | { 109 | "id": "8:2875693831440918677", 110 | "name": "simpleDouble", 111 | "type": 8 112 | }, 113 | { 114 | "id": "9:7453429302134663068", 115 | "name": "simpleString", 116 | "indexId": "2:2825443978184014074", 117 | "type": 9, 118 | "flags": 2048 119 | }, 120 | { 121 | "id": "10:7622112042154959206", 122 | "name": "simpleByteArray", 123 | "type": 23 124 | } 125 | ], 126 | "relations": [] 127 | } 128 | ], 129 | "lastEntityId": "2:1960446593108937414", 130 | "lastIndexId": "2:2825443978184014074", 131 | "lastRelationId": "0:0", 132 | "lastSequenceId": "0:0", 133 | "modelVersion": 5, 134 | "modelVersionParserMinimum": 5, 135 | "retiredEntityUids": [], 136 | "retiredIndexUids": [], 137 | "retiredPropertyUids": [], 138 | "retiredRelationUids": [], 139 | "version": 1 140 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Java\adt\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/schemas/io.objectbox.performanceapp.room.AppDatabase/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "3440b72ef8e427f23f501299b3de69d9", 6 | "entities": [ 7 | { 8 | "tableName": "SimpleEntity", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `simpleBoolean` INTEGER NOT NULL, `simpleByte` INTEGER NOT NULL, `simpleShort` INTEGER NOT NULL, `simpleInt` INTEGER NOT NULL, `simpleLong` INTEGER NOT NULL, `simpleFloat` REAL NOT NULL, `simpleDouble` REAL NOT NULL, `simpleString` TEXT, `simpleByteArray` BLOB, PRIMARY KEY(`id`))", 10 | "fields": [ 11 | { 12 | "fieldPath": "id", 13 | "columnName": "id", 14 | "affinity": "INTEGER", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "simpleBoolean", 19 | "columnName": "simpleBoolean", 20 | "affinity": "INTEGER", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "simpleByte", 25 | "columnName": "simpleByte", 26 | "affinity": "INTEGER", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "simpleShort", 31 | "columnName": "simpleShort", 32 | "affinity": "INTEGER", 33 | "notNull": true 34 | }, 35 | { 36 | "fieldPath": "simpleInt", 37 | "columnName": "simpleInt", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | }, 41 | { 42 | "fieldPath": "simpleLong", 43 | "columnName": "simpleLong", 44 | "affinity": "INTEGER", 45 | "notNull": true 46 | }, 47 | { 48 | "fieldPath": "simpleFloat", 49 | "columnName": "simpleFloat", 50 | "affinity": "REAL", 51 | "notNull": true 52 | }, 53 | { 54 | "fieldPath": "simpleDouble", 55 | "columnName": "simpleDouble", 56 | "affinity": "REAL", 57 | "notNull": true 58 | }, 59 | { 60 | "fieldPath": "simpleString", 61 | "columnName": "simpleString", 62 | "affinity": "TEXT", 63 | "notNull": false 64 | }, 65 | { 66 | "fieldPath": "simpleByteArray", 67 | "columnName": "simpleByteArray", 68 | "affinity": "BLOB", 69 | "notNull": false 70 | } 71 | ], 72 | "primaryKey": { 73 | "columnNames": [ 74 | "id" 75 | ], 76 | "autoGenerate": false 77 | }, 78 | "indices": [], 79 | "foreignKeys": [] 80 | }, 81 | { 82 | "tableName": "SimpleEntityIndexed", 83 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `simpleBoolean` INTEGER NOT NULL, `simpleByte` INTEGER NOT NULL, `simpleShort` INTEGER NOT NULL, `simpleInt` INTEGER NOT NULL, `simpleLong` INTEGER NOT NULL, `simpleFloat` REAL NOT NULL, `simpleDouble` REAL NOT NULL, `simpleString` TEXT, `simpleByteArray` BLOB, PRIMARY KEY(`id`))", 84 | "fields": [ 85 | { 86 | "fieldPath": "id", 87 | "columnName": "id", 88 | "affinity": "INTEGER", 89 | "notNull": true 90 | }, 91 | { 92 | "fieldPath": "simpleBoolean", 93 | "columnName": "simpleBoolean", 94 | "affinity": "INTEGER", 95 | "notNull": true 96 | }, 97 | { 98 | "fieldPath": "simpleByte", 99 | "columnName": "simpleByte", 100 | "affinity": "INTEGER", 101 | "notNull": true 102 | }, 103 | { 104 | "fieldPath": "simpleShort", 105 | "columnName": "simpleShort", 106 | "affinity": "INTEGER", 107 | "notNull": true 108 | }, 109 | { 110 | "fieldPath": "simpleInt", 111 | "columnName": "simpleInt", 112 | "affinity": "INTEGER", 113 | "notNull": true 114 | }, 115 | { 116 | "fieldPath": "simpleLong", 117 | "columnName": "simpleLong", 118 | "affinity": "INTEGER", 119 | "notNull": true 120 | }, 121 | { 122 | "fieldPath": "simpleFloat", 123 | "columnName": "simpleFloat", 124 | "affinity": "REAL", 125 | "notNull": true 126 | }, 127 | { 128 | "fieldPath": "simpleDouble", 129 | "columnName": "simpleDouble", 130 | "affinity": "REAL", 131 | "notNull": true 132 | }, 133 | { 134 | "fieldPath": "simpleString", 135 | "columnName": "simpleString", 136 | "affinity": "TEXT", 137 | "notNull": false 138 | }, 139 | { 140 | "fieldPath": "simpleByteArray", 141 | "columnName": "simpleByteArray", 142 | "affinity": "BLOB", 143 | "notNull": false 144 | } 145 | ], 146 | "primaryKey": { 147 | "columnNames": [ 148 | "id" 149 | ], 150 | "autoGenerate": false 151 | }, 152 | "indices": [ 153 | { 154 | "name": "index_SimpleEntityIndexed_simpleInt", 155 | "unique": false, 156 | "columnNames": [ 157 | "simpleInt" 158 | ], 159 | "createSql": "CREATE INDEX `index_SimpleEntityIndexed_simpleInt` ON `${TABLE_NAME}` (`simpleInt`)" 160 | }, 161 | { 162 | "name": "index_SimpleEntityIndexed_simpleString", 163 | "unique": false, 164 | "columnNames": [ 165 | "simpleString" 166 | ], 167 | "createSql": "CREATE INDEX `index_SimpleEntityIndexed_simpleString` ON `${TABLE_NAME}` (`simpleString`)" 168 | } 169 | ], 170 | "foreignKeys": [] 171 | } 172 | ], 173 | "setupQueries": [ 174 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 175 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"3440b72ef8e427f23f501299b3de69d9\")" 176 | ] 177 | } 178 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/Benchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp; 18 | 19 | import android.os.Build; 20 | import android.os.SystemClock; 21 | import android.util.Log; 22 | import android.util.Pair; 23 | 24 | import org.greenrobot.essentials.StringUtils; 25 | import org.greenrobot.essentials.io.FileUtils; 26 | 27 | import java.io.File; 28 | import java.io.FileNotFoundException; 29 | import java.io.IOException; 30 | import java.text.SimpleDateFormat; 31 | import java.util.ArrayList; 32 | import java.util.Arrays; 33 | import java.util.Date; 34 | import java.util.List; 35 | 36 | public class Benchmark { 37 | public static final String TAG = "Benchmark"; 38 | 39 | private final List> fixedColumns = new ArrayList<>(); 40 | private final List> values = new ArrayList<>(); 41 | private final File file; 42 | private final SimpleDateFormat dateFormat; 43 | private final char separator = '\t'; 44 | 45 | private String[] headers; 46 | private boolean storeThreadTime; 47 | 48 | private boolean started; 49 | private long threadTimeMillis; 50 | private long timeMillis; 51 | private String name; 52 | private int runs; 53 | private int warmUpRuns; 54 | 55 | public Benchmark(File file) { 56 | this.file = file; 57 | dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 58 | checkForLastHeader(file); 59 | } 60 | 61 | private void checkForLastHeader(File file) { 62 | String contents = null; 63 | try { 64 | contents = FileUtils.readUtf8(file); 65 | } catch (FileNotFoundException e) { 66 | // OK 67 | } catch (IOException e) { 68 | throw new RuntimeException(e); 69 | } 70 | if (contents == null) { 71 | return; 72 | } 73 | 74 | String[] lines = StringUtils.split(contents, '\n'); 75 | for (int i = lines.length - 1; i >= 0; i--) { 76 | String[] columnValues = StringUtils.split(lines[i], separator); 77 | if (columnValues.length > 1) { 78 | boolean longValueFound = false; 79 | for (String value : columnValues) { 80 | try { 81 | Long.parseLong(value); 82 | longValueFound = true; 83 | break; 84 | } catch (NumberFormatException e) { 85 | // OK, header candidate 86 | } 87 | } 88 | if (!longValueFound) { 89 | headers = columnValues; 90 | break; 91 | } 92 | } 93 | } 94 | } 95 | 96 | public Benchmark warmUpRuns(int warmUpRuns) { 97 | this.warmUpRuns = warmUpRuns; 98 | return this; 99 | } 100 | 101 | public Benchmark enableThreadTime() { 102 | this.storeThreadTime = true; 103 | return this; 104 | } 105 | 106 | public Benchmark disableThreadTime() { 107 | this.storeThreadTime = false; 108 | return this; 109 | } 110 | 111 | public Benchmark addFixedColumn(String key, String value) { 112 | fixedColumns.add(new Pair(key, value)); 113 | return this; 114 | } 115 | 116 | public Benchmark addFixedColumnDevice() { 117 | addFixedColumn("device", Build.MODEL); 118 | return this; 119 | } 120 | 121 | public void start(String name) { 122 | if (started) { 123 | throw new RuntimeException("Already started"); 124 | } 125 | started = true; 126 | prepareForNextRun(); 127 | if (values.isEmpty()) { 128 | values.addAll(fixedColumns); 129 | String startTime = dateFormat.format(new Date()); 130 | values.add(new Pair<>("time", startTime)); 131 | } 132 | this.name = name; 133 | threadTimeMillis = SystemClock.currentThreadTimeMillis(); 134 | timeMillis = SystemClock.elapsedRealtime(); 135 | } 136 | 137 | /** 138 | * Try to give GC & finalization some time to settle down. 139 | */ 140 | public void prepareForNextRun() { 141 | for (int i = 0; i < 5; i++) { 142 | System.gc(); 143 | System.runFinalization(); 144 | try { 145 | Thread.sleep(20); 146 | } catch (InterruptedException e) { 147 | e.printStackTrace(); 148 | } 149 | } 150 | } 151 | 152 | public String stop() { 153 | long time = SystemClock.elapsedRealtime() - timeMillis; 154 | long timeThread = SystemClock.currentThreadTimeMillis() - threadTimeMillis; 155 | if (!started) { 156 | throw new RuntimeException("Not started"); 157 | } 158 | started = false; 159 | 160 | String logMessage = name + ": " + time + " ms (thread: " + timeThread + " ms)"; 161 | values.add(new Pair<>(name, Long.toString(time))); 162 | if (storeThreadTime) { 163 | values.add(new Pair<>(name + "-thread", Long.toString(timeThread))); 164 | } 165 | name = null; 166 | return logMessage; 167 | } 168 | 169 | public void commit() { 170 | runs++; 171 | if (runs > warmUpRuns) { 172 | Log.d(TAG, "Writing results for run " + runs); 173 | String[] collectedHeaders = getAllFirsts(values); 174 | if (!Arrays.equals(collectedHeaders, headers)) { 175 | headers = collectedHeaders; 176 | String line = StringUtils.join(headers, "" + separator) + '\n'; 177 | try { 178 | FileUtils.appendUtf8(file, line); 179 | } catch (IOException e) { 180 | throw new RuntimeException("Could not write header in benchmark file", e); 181 | } 182 | } 183 | 184 | StringBuilder line = new StringBuilder(); 185 | for (Pair pair : values) { 186 | line.append(pair.second).append(separator); 187 | } 188 | line.append('\n'); 189 | try { 190 | FileUtils.appendUtf8(file, line); 191 | } catch (IOException e) { 192 | throw new RuntimeException("Could not write header in benchmark file", e); 193 | } 194 | } else { 195 | Log.d(TAG, "Ignoring results for run " + runs + " (warm up)"); 196 | } 197 | values.clear(); 198 | } 199 | 200 | private String[] getAllFirsts(List> columns) { 201 | String[] firsts = new String[columns.size()]; 202 | for (int i = 0; i < firsts.length; i++) { 203 | firsts[i] = columns.get(i).first; 204 | } 205 | return firsts; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2024 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp; 18 | 19 | import android.app.Activity; 20 | import android.content.Context; 21 | import android.content.SharedPreferences; 22 | import android.os.Bundle; 23 | import android.view.View; 24 | import android.view.inputmethod.InputMethodManager; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.EditText; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | import androidx.preference.PreferenceManager; 32 | import io.objectbox.performanceapp.PerfTestRunner.Callback; 33 | import io.objectbox.performanceapp.databinding.ActivityMainBinding; 34 | import io.objectbox.performanceapp.greendao.GreendaoPerfTest; 35 | import io.objectbox.performanceapp.objectbox.ObjectBoxPerfTest; 36 | import io.objectbox.performanceapp.realm.RealmPerfTest; 37 | import io.objectbox.performanceapp.room.RoomPerfTest; 38 | 39 | public class MainActivity extends Activity implements Callback { 40 | 41 | private static final String PREF_TYPE = "io.objectbox.performance.type"; 42 | private static final String PREF_RUNS = "io.objectbox.performance.runs"; 43 | private static final String PREF_COUNT = "io.objectbox.performance.count"; 44 | 45 | private ActivityMainBinding binding; 46 | private PerfTestRunner testRunner; 47 | 48 | @Override 49 | protected void onCreate(Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | binding = ActivityMainBinding.inflate(getLayoutInflater()); 52 | setContentView(binding.getRoot()); 53 | 54 | binding.buttonRunTest.setOnClickListener(view -> { 55 | binding.buttonRunTest.setEnabled(false); 56 | View currentFocus = MainActivity.this.getCurrentFocus(); 57 | if (currentFocus != null) { 58 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 59 | if (imm != null) { 60 | imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0); 61 | } 62 | currentFocus.clearFocus(); 63 | } 64 | boolean objectBox = binding.checkBoxObjectBox.isChecked(); 65 | boolean realm = binding.checkBoxRealm.isChecked(); 66 | boolean greenDao = binding.checkBoxGreenDao.isChecked(); 67 | boolean room = binding.checkBoxRoom.isChecked(); 68 | TestType type = (TestType) binding.spinnerTestType.getSelectedItem(); 69 | 70 | int runs = getIntegerFromEditTextOrZero(binding.editTextRuns); 71 | int numberEntities = getIntegerFromEditTextOrZero(binding.editTextNumberEntities); 72 | 73 | runTests(type, runs, numberEntities, objectBox, realm, greenDao, room); 74 | }); 75 | 76 | ArrayAdapter adapter = new ArrayAdapter<>( 77 | this, 78 | android.R.layout.simple_spinner_item, 79 | TestType.ALL 80 | ); 81 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 82 | binding.spinnerTestType.setAdapter(adapter); 83 | 84 | // Restore type, runs and count or set defaults. 85 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 86 | int previousTypeSelection = prefs.getInt(PREF_TYPE, 0); 87 | if (previousTypeSelection > TestType.ALL.length - 1 || previousTypeSelection < 0) { 88 | previousTypeSelection = 0; 89 | } 90 | binding.spinnerTestType 91 | .setSelection(previousTypeSelection, false); 92 | binding.editTextRuns 93 | .setText(String.valueOf(prefs.getInt(PREF_RUNS, 1))); 94 | binding.editTextNumberEntities 95 | .setText(String.valueOf(prefs.getInt(PREF_COUNT, 100000))); 96 | } 97 | 98 | private int getIntegerFromEditTextOrZero(EditText editText) { 99 | try { 100 | return Integer.parseInt(editText.getText().toString()); 101 | } catch (NumberFormatException e) { 102 | binding.textViewResults.append(e.getMessage() + "\n"); 103 | return 0; 104 | } 105 | } 106 | 107 | @Override 108 | protected void onPause() { 109 | super.onPause(); 110 | // Save type, runs and count. 111 | PreferenceManager.getDefaultSharedPreferences(this).edit() 112 | .putInt(PREF_TYPE, binding.spinnerTestType.getSelectedItemPosition()) 113 | .putInt(PREF_RUNS, 114 | getIntegerFromEditTextOrZero(binding.editTextRuns)) 115 | .putInt(PREF_COUNT, 116 | getIntegerFromEditTextOrZero(binding.editTextNumberEntities)) 117 | .apply(); 118 | } 119 | 120 | @Override 121 | protected void onDestroy() { 122 | if (testRunner != null) { 123 | testRunner.destroy(); 124 | } 125 | testRunner = null; 126 | super.onDestroy(); 127 | } 128 | 129 | private void runTests(TestType type, int runs, int numberEntities, boolean objectBox, boolean realm, boolean greenDao, boolean room) { 130 | binding.textViewResults.setText(""); 131 | List tests = new ArrayList<>(); 132 | if (objectBox) { 133 | tests.add(new ObjectBoxPerfTest()); 134 | } 135 | if (realm) { 136 | tests.add(new RealmPerfTest()); 137 | } 138 | if (greenDao) { 139 | tests.add(new GreendaoPerfTest()); 140 | } 141 | if (room) { 142 | tests.add(new RoomPerfTest()); 143 | } 144 | testRunner = new PerfTestRunner(this, this, binding.textViewResults, runs, numberEntities); 145 | testRunner.run(type, tests); 146 | } 147 | 148 | @Override 149 | public void done() { 150 | testRunner = null; 151 | runOnUiThread(() -> binding.buttonRunTest.setEnabled(true)); 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/PerfTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp; 18 | 19 | import android.content.Context; 20 | import androidx.annotation.CallSuper; 21 | 22 | import java.util.Random; 23 | 24 | public abstract class PerfTest { 25 | 26 | protected Random random; 27 | protected Context context; 28 | protected PerfTestRunner testRunner; 29 | protected int numberEntities; 30 | protected Benchmark benchmark; 31 | 32 | @CallSuper 33 | public void setUp(Context context, PerfTestRunner testRunner) { 34 | random = new Random(); 35 | this.context = context.getApplicationContext(); 36 | this.testRunner = testRunner; 37 | } 38 | 39 | public void tearDown() { 40 | } 41 | 42 | protected void log(String text) { 43 | testRunner.log(text); 44 | } 45 | 46 | public abstract String name(); 47 | 48 | public abstract void run(TestType type); 49 | 50 | public void setNumberEntities(int numberEntities) { 51 | this.numberEntities = numberEntities; 52 | } 53 | 54 | public void setBenchmark(Benchmark benchmark) { 55 | this.benchmark = benchmark; 56 | } 57 | 58 | protected void startBenchmark(String name) { 59 | benchmark.start(name); 60 | } 61 | 62 | protected void stopBenchmark() { 63 | log(benchmark.stop()); 64 | } 65 | 66 | /** 67 | * Convenience for {@link #startBenchmark(String)} followed by {@link #stopBenchmark()}. 68 | */ 69 | protected void benchmark(String name, Runnable runnable) { 70 | startBenchmark(name); 71 | runnable.run(); 72 | stopBenchmark(); 73 | } 74 | 75 | public String randomString() { 76 | return RandomValues.createRandomString(random, 0, 100); 77 | } 78 | 79 | public byte[] randomBytes() { 80 | int length = random.nextInt(100); 81 | byte[] bytes = new byte[length]; 82 | random.nextBytes(bytes); 83 | return bytes; 84 | } 85 | 86 | public void allTestsComplete() { 87 | } 88 | 89 | protected void assertEntityCount(long size) { 90 | if (size != numberEntities) { 91 | throw new IllegalStateException("Expected " + numberEntities + " but actual number is " + size); 92 | } 93 | } 94 | 95 | protected void assertGreaterOrEqualToNumberOfEntities(long count) { 96 | if (count < numberEntities) { 97 | throw new IllegalStateException("Expected at least " + numberEntities + " but actual number is " + count); 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/PerfTestRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp; 18 | 19 | import android.app.Activity; 20 | import android.app.ActivityManager; 21 | import android.graphics.Color; 22 | import android.os.Build; 23 | import android.os.Environment; 24 | import android.text.Spannable; 25 | import android.text.SpannableString; 26 | import android.text.style.ForegroundColorSpan; 27 | import android.util.Log; 28 | import android.widget.ScrollView; 29 | import android.widget.TextView; 30 | 31 | import java.io.File; 32 | import java.util.Date; 33 | import java.util.List; 34 | import java.util.concurrent.CountDownLatch; 35 | import java.util.concurrent.TimeUnit; 36 | 37 | /** 38 | * Created by Markus on 01.10.2016. 39 | */ 40 | 41 | public class PerfTestRunner { 42 | 43 | interface Callback { 44 | void done(); 45 | } 46 | 47 | private final Activity activity; 48 | private final Callback callback; 49 | private final TextView textViewResults; 50 | private final int runs; 51 | private final int numberEntities; 52 | private ScrollView scrollViewResults; 53 | 54 | boolean running; 55 | boolean destroyed; 56 | 57 | public PerfTestRunner(Activity activity, Callback callback, TextView textViewResults, int runs, int numberEntities) { 58 | this.activity = activity; 59 | this.callback = callback; 60 | this.textViewResults = textViewResults; 61 | if (textViewResults.getParent() instanceof ScrollView) { 62 | scrollViewResults = (ScrollView) textViewResults.getParent(); 63 | } 64 | this.runs = runs; 65 | this.numberEntities = numberEntities; 66 | } 67 | 68 | public void run(final TestType type, final List tests) { 69 | if (running) { 70 | throw new IllegalStateException("Already running"); 71 | } 72 | running = true; 73 | Thread thread = new Thread(() -> { 74 | try { 75 | for (PerfTest test : tests) { 76 | if (!destroyed) { 77 | try { 78 | PerfTestRunner.this.run(type, test); 79 | } catch (Exception e) { 80 | logError("Aborted because of " + e.getMessage()); 81 | Log.e("PERF", "Error while running tests", e); 82 | } 83 | } 84 | } 85 | } finally { 86 | running = false; 87 | callback.done(); 88 | } 89 | }); 90 | thread.setPriority(Thread.MAX_PRIORITY); 91 | thread.start(); 92 | } 93 | 94 | public void destroy() { 95 | destroyed = true; 96 | } 97 | 98 | public void log(final String text) { 99 | log(text, false); 100 | } 101 | 102 | public void logError(final String text) { 103 | log(text, true); 104 | } 105 | 106 | private void log(final String text, final boolean error) { 107 | Log.d("PERF", text); 108 | final CountDownLatch joinLatch = new CountDownLatch(1); 109 | activity.runOnUiThread(() -> { 110 | if (error) { 111 | Spannable errorSpan = new SpannableString(text.concat("\n")); 112 | errorSpan.setSpan(new ForegroundColorSpan(Color.RED), 0, errorSpan.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 113 | textViewResults.append(errorSpan); 114 | } else { 115 | textViewResults.append(text.concat("\n")); 116 | } 117 | // post so just appended text is visible 118 | if (scrollViewResults != null) { 119 | textViewResults.post(() -> scrollViewResults.fullScroll(ScrollView.FOCUS_DOWN)); 120 | } 121 | textViewResults.postDelayed(joinLatch::countDown, 20); 122 | }); 123 | try { 124 | boolean ok = joinLatch.await(10, TimeUnit.SECONDS); 125 | if (!ok) { 126 | throw new RuntimeException("Not joined"); 127 | } 128 | // Give UI time to settle (> 1 frame) 129 | Thread.sleep(20); 130 | } catch (InterruptedException e) { 131 | throw new RuntimeException(e); 132 | } 133 | } 134 | 135 | private void run(TestType type, PerfTest test) { 136 | printDeviceInfo(); 137 | 138 | test.setNumberEntities(numberEntities); 139 | Benchmark benchmark = createBenchmark(type, test, numberEntities); 140 | test.setBenchmark(benchmark); 141 | log("\nStarting tests with " + numberEntities + " entities at " + new Date()); 142 | for (int i = 1; i <= runs; i++) { 143 | log("\n" + test.name() + " " + type + " (" + i + "/" + runs + ")\n" + 144 | "------------------------------"); 145 | test.setUp(activity, this); 146 | 147 | RuntimeException exDuringRun = null; 148 | try { 149 | test.run(type); 150 | } catch (RuntimeException ex) { 151 | exDuringRun = ex; 152 | } 153 | 154 | RuntimeException exDuringTearDown = null; 155 | try { 156 | test.tearDown(); 157 | } catch (RuntimeException ex) { 158 | exDuringTearDown = ex; 159 | } 160 | if (exDuringRun != null) { 161 | throw exDuringRun; 162 | } else if (exDuringTearDown != null) { 163 | throw exDuringTearDown; 164 | } 165 | benchmark.commit(); 166 | if (destroyed) { 167 | break; 168 | } 169 | } 170 | test.allTestsComplete(); 171 | log("\nTests done at " + new Date()); 172 | } 173 | 174 | private void printDeviceInfo() { 175 | log("Model: " + Build.MANUFACTURER + " " + Build.MODEL 176 | + ", Android " + Build.VERSION.RELEASE); 177 | 178 | ActivityManager activityManager = 179 | (ActivityManager) activity.getSystemService(Activity.ACTIVITY_SERVICE); 180 | int memoryClassMb = activityManager.getMemoryClass(); 181 | int largeMemoryClassMb = activityManager.getLargeMemoryClass(); 182 | log("MemoryClass: " + memoryClassMb + " MB"); 183 | log("LargeMemoryClass: " + largeMemoryClassMb + " MB"); 184 | } 185 | 186 | protected Benchmark createBenchmark(TestType type, PerfTest test, int numberEntities) { 187 | String name = test.name() + "-" + type.nameShort + "-" + numberEntities + ".tsv"; 188 | File dir = Environment.getExternalStorageDirectory(); 189 | File file = new File(dir, name); 190 | if (dir == null || !dir.canWrite()) { 191 | File appFile = new File(activity.getFilesDir(), name); 192 | Log.i("PERF", "Using file " + appFile.getAbsolutePath() + " because " + file.getAbsolutePath() + 193 | " is not writable - please grant the storage permission to the app"); 194 | file = appFile; 195 | } 196 | return new Benchmark(file); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/RandomValues.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp; 18 | 19 | import java.util.Random; 20 | 21 | /** 22 | * Helper class to generate a pre-determined set of random values (strings/ints) . 23 | */ 24 | public class RandomValues { 25 | 26 | // Fixed seed so we generate the same set of strings every time. 27 | public static final long SEED = -2662502316022774L; 28 | private static final int MIN_LENGTH = 5; 29 | private static final int MAX_LENGTH = 500; 30 | 31 | // limit to a fixed set of chars 32 | private static final char[] CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 33 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 34 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 35 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 36 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; 37 | 38 | /** 39 | * Creates the same random sequence of strings. 40 | */ 41 | public static String[] createFixedRandomStrings(int count) { 42 | return createFixedRandomStrings(count, MIN_LENGTH, MAX_LENGTH); 43 | } 44 | 45 | public static String[] createFixedRandomStrings(int count, int minLength, int maxLength) { 46 | String[] strings = new String[count]; 47 | Random random = new Random(SEED); 48 | for (int i = 0; i < count; i++) { 49 | strings[i] = createRandomString(random, minLength, maxLength); 50 | } 51 | return strings; 52 | } 53 | 54 | public static String createRandomString(Random random, int minLength, int maxLength) { 55 | int length = minLength + random.nextInt(maxLength - minLength); 56 | return createRandomString(random, length); 57 | } 58 | 59 | public static String createRandomString(Random random, int length) { 60 | char[] chars = new char[length + 4]; 61 | for (int i = 0; i < length; ) { 62 | int intVal = random.nextInt(); 63 | chars[i++] = CHARS[((intVal & 0xff) % CHARS.length)]; 64 | chars[i++] = CHARS[(((intVal >> 8) & 0xff) % CHARS.length)]; 65 | chars[i++] = CHARS[(((intVal >> 16) & 0xff) % CHARS.length)]; 66 | chars[i++] = CHARS[(((intVal >> 24) & 0xff) % CHARS.length)]; 67 | } 68 | return new String(chars, 0, length); 69 | } 70 | 71 | /** 72 | * Creates the same random sequence of indexes. To be used to select strings by {@link 73 | * #createFixedRandomStrings(int)}. 74 | */ 75 | public static int[] createFixedRandomInts(int count, int maxInt) { 76 | int[] ints = new int[count]; 77 | Random random = new Random(SEED); 78 | for (int i = 0; i < count; i++) { 79 | ints[i] = random.nextInt(maxInt + 1); 80 | } 81 | return ints; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/TestType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp; 18 | 19 | /** 20 | * Created by Markus on 01.10.2016. 21 | */ 22 | public class TestType { 23 | public static final String CRUD = "Basic operations (CRUD)"; 24 | public static final String CRUD_SCALARS = "Basic operations (CRUD) - scalars"; 25 | public static final String CRUD_INDEXED = "Basic operations (CRUD) - indexed"; 26 | public static final String QUERY_STRING = "Query by string"; 27 | public static final String QUERY_STRING_INDEXED = "Query by string - indexed"; 28 | public static final String QUERY_INTEGER = "Query by integer"; 29 | public static final String QUERY_INTEGER_INDEXED = "Query by integer - indexed"; 30 | public static final String QUERY_ID = "Query by ID"; 31 | public static final String QUERY_ID_RANDOM = "Query by ID - random"; 32 | 33 | public static TestType[] ALL = { 34 | new TestType(CRUD, "crud"), 35 | new TestType(CRUD_SCALARS, "crud-scalars"), 36 | new TestType(CRUD_INDEXED, "crud-indexed"), 37 | new TestType(QUERY_STRING, "query-string"), 38 | new TestType(QUERY_STRING_INDEXED, "query-string-indexed"), 39 | new TestType(QUERY_INTEGER, "query-integer"), 40 | new TestType(QUERY_INTEGER_INDEXED, "query-integer-indexed"), 41 | new TestType(QUERY_ID, "query-id"), 42 | new TestType(QUERY_ID_RANDOM, "query-id-random"), 43 | }; 44 | 45 | public final String name; 46 | public final String nameShort; 47 | 48 | public TestType(String name, String nameShort) { 49 | this.name = name; 50 | this.nameShort = nameShort; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return name; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/greendao/DaoMaster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.greendao; 18 | 19 | import android.content.Context; 20 | import android.database.sqlite.SQLiteDatabase; 21 | import android.database.sqlite.SQLiteDatabase.CursorFactory; 22 | import android.util.Log; 23 | 24 | import org.greenrobot.greendao.AbstractDaoMaster; 25 | import org.greenrobot.greendao.database.StandardDatabase; 26 | import org.greenrobot.greendao.database.Database; 27 | import org.greenrobot.greendao.database.DatabaseOpenHelper; 28 | import org.greenrobot.greendao.identityscope.IdentityScopeType; 29 | 30 | 31 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. 32 | /** 33 | * Master of DAO (schema version 1): knows all DAOs. 34 | */ 35 | public class DaoMaster extends AbstractDaoMaster { 36 | public static final int SCHEMA_VERSION = 1; 37 | 38 | /** Creates underlying database table using DAOs. */ 39 | public static void createAllTables(Database db, boolean ifNotExists) { 40 | SimpleEntityDao.createTable(db, ifNotExists); 41 | SimpleEntityIndexedDao.createTable(db, ifNotExists); 42 | } 43 | 44 | /** Drops underlying database table using DAOs. */ 45 | public static void dropAllTables(Database db, boolean ifExists) { 46 | SimpleEntityDao.dropTable(db, ifExists); 47 | SimpleEntityIndexedDao.dropTable(db, ifExists); 48 | } 49 | 50 | /** 51 | * WARNING: Drops all table on Upgrade! Use only during development. 52 | * Convenience method using a {@link DevOpenHelper}. 53 | */ 54 | public static DaoSession newDevSession(Context context, String name) { 55 | Database db = new DevOpenHelper(context, name).getWritableDb(); 56 | DaoMaster daoMaster = new DaoMaster(db); 57 | return daoMaster.newSession(); 58 | } 59 | 60 | public DaoMaster(SQLiteDatabase db) { 61 | this(new StandardDatabase(db)); 62 | } 63 | 64 | public DaoMaster(Database db) { 65 | super(db, SCHEMA_VERSION); 66 | registerDaoClass(SimpleEntityDao.class); 67 | registerDaoClass(SimpleEntityIndexedDao.class); 68 | } 69 | 70 | public DaoSession newSession() { 71 | return new DaoSession(db, IdentityScopeType.Session, daoConfigMap); 72 | } 73 | 74 | public DaoSession newSession(IdentityScopeType type) { 75 | return new DaoSession(db, type, daoConfigMap); 76 | } 77 | 78 | /** 79 | * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} - 80 | */ 81 | public static abstract class OpenHelper extends DatabaseOpenHelper { 82 | public OpenHelper(Context context, String name) { 83 | super(context, name, SCHEMA_VERSION); 84 | } 85 | 86 | public OpenHelper(Context context, String name, CursorFactory factory) { 87 | super(context, name, factory, SCHEMA_VERSION); 88 | } 89 | 90 | @Override 91 | public void onCreate(Database db) { 92 | Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION); 93 | createAllTables(db, false); 94 | } 95 | } 96 | 97 | /** WARNING: Drops all table on Upgrade! Use only during development. */ 98 | public static class DevOpenHelper extends OpenHelper { 99 | public DevOpenHelper(Context context, String name) { 100 | super(context, name); 101 | } 102 | 103 | public DevOpenHelper(Context context, String name, CursorFactory factory) { 104 | super(context, name, factory); 105 | } 106 | 107 | @Override 108 | public void onUpgrade(Database db, int oldVersion, int newVersion) { 109 | Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); 110 | dropAllTables(db, true); 111 | onCreate(db); 112 | } 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/greendao/DaoSession.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.greendao; 18 | 19 | import org.greenrobot.greendao.AbstractDao; 20 | import org.greenrobot.greendao.AbstractDaoSession; 21 | import org.greenrobot.greendao.database.Database; 22 | import org.greenrobot.greendao.identityscope.IdentityScopeType; 23 | import org.greenrobot.greendao.internal.DaoConfig; 24 | 25 | import java.util.Map; 26 | 27 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. 28 | 29 | /** 30 | * {@inheritDoc} 31 | * 32 | * @see org.greenrobot.greendao.AbstractDaoSession 33 | */ 34 | public class DaoSession extends AbstractDaoSession { 35 | 36 | private final DaoConfig simpleEntityDaoConfig; 37 | private final DaoConfig simpleEntityIndexedDaoConfig; 38 | 39 | private final SimpleEntityDao simpleEntityDao; 40 | private final SimpleEntityIndexedDao simpleEntityIndexedDao; 41 | 42 | public DaoSession(Database db, IdentityScopeType type, Map>, DaoConfig> 43 | daoConfigMap) { 44 | super(db); 45 | 46 | simpleEntityDaoConfig = daoConfigMap.get(SimpleEntityDao.class).clone(); 47 | simpleEntityDaoConfig.initIdentityScope(type); 48 | 49 | simpleEntityIndexedDaoConfig = daoConfigMap.get(SimpleEntityIndexedDao.class).clone(); 50 | simpleEntityIndexedDaoConfig.initIdentityScope(type); 51 | 52 | simpleEntityDao = new SimpleEntityDao(simpleEntityDaoConfig, this); 53 | simpleEntityIndexedDao = new SimpleEntityIndexedDao(simpleEntityIndexedDaoConfig, this); 54 | 55 | registerDao(SimpleEntity.class, simpleEntityDao); 56 | registerDao(SimpleEntityIndexed.class, simpleEntityIndexedDao); 57 | } 58 | 59 | public void clear() { 60 | simpleEntityDaoConfig.clearIdentityScope(); 61 | simpleEntityIndexedDaoConfig.clearIdentityScope(); 62 | } 63 | 64 | public SimpleEntityDao getSimpleEntityDao() { 65 | return simpleEntityDao; 66 | } 67 | 68 | public SimpleEntityIndexedDao getSimpleEntityIndexedDao() { 69 | return simpleEntityIndexedDao; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/greendao/GreendaoPerfTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.greendao; 18 | 19 | import android.content.Context; 20 | import android.database.Cursor; 21 | 22 | import org.greenrobot.greendao.database.Database; 23 | import org.greenrobot.greendao.identityscope.IdentityScopeType; 24 | import org.greenrobot.greendao.query.Query; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import io.objectbox.performanceapp.PerfTest; 30 | import io.objectbox.performanceapp.PerfTestRunner; 31 | import io.objectbox.performanceapp.TestType; 32 | import io.objectbox.performanceapp.greendao.DaoMaster.DevOpenHelper; 33 | import io.objectbox.performanceapp.greendao.SimpleEntityIndexedDao.Properties; 34 | 35 | public class GreendaoPerfTest extends PerfTest { 36 | public static final String DB_NAME = "sqlite-greendao"; 37 | 38 | private Database db; 39 | private DaoSession daoSession; 40 | private SimpleEntityDao dao; 41 | 42 | private boolean versionLoggedOnce; 43 | private SimpleEntityIndexedDao daoIndexed; 44 | 45 | @Override 46 | public String name() { 47 | return "greenDAO"; 48 | } 49 | 50 | public void setUp(Context context, PerfTestRunner testRunner) { 51 | super.setUp(context, testRunner); 52 | boolean deleted = context.deleteDatabase(DB_NAME); 53 | if (deleted) { 54 | log("DB existed before start - deleted"); 55 | } 56 | db = new DevOpenHelper(context, DB_NAME).getWritableDb(); 57 | daoSession = new DaoMaster(db).newSession(IdentityScopeType.None); 58 | dao = daoSession.getSimpleEntityDao(); 59 | daoIndexed = daoSession.getSimpleEntityIndexedDao(); 60 | 61 | if (!versionLoggedOnce) { 62 | Cursor cursor = db.rawQuery("select sqlite_version() AS sqlite_version", null); 63 | if (cursor != null) { 64 | try { 65 | if (cursor.moveToFirst()) { 66 | log("SQLite version " + cursor.getString(0)); 67 | } 68 | } finally { 69 | cursor.close(); 70 | } 71 | } 72 | versionLoggedOnce = true; 73 | } 74 | } 75 | 76 | @Override 77 | public void run(TestType type) { 78 | switch (type.name) { 79 | case TestType.CRUD: 80 | runBatchPerfTest(false); 81 | break; 82 | case TestType.CRUD_SCALARS: 83 | runBatchPerfTest(true); 84 | break; 85 | case TestType.CRUD_INDEXED: 86 | runBatchPerfTestIndexed(); 87 | break; 88 | case TestType.QUERY_STRING: 89 | runQueryByString(); 90 | break; 91 | case TestType.QUERY_STRING_INDEXED: 92 | runQueryByStringIndexed(); 93 | break; 94 | case TestType.QUERY_ID: 95 | runQueryById(false); 96 | break; 97 | case TestType.QUERY_ID_RANDOM: 98 | runQueryById(true); 99 | break; 100 | } 101 | } 102 | 103 | public void runBatchPerfTest(boolean scalarsOnly) { 104 | List list = new ArrayList<>(numberEntities); 105 | for (int i = 0; i < numberEntities; i++) { 106 | list.add(createEntity((long) i, scalarsOnly)); 107 | } 108 | startBenchmark("insert"); 109 | dao.insertInTx(list); 110 | stopBenchmark(); 111 | 112 | for (SimpleEntity entity : list) { 113 | if (scalarsOnly) { 114 | setRandomScalars(entity); 115 | } else { 116 | setRandomValues(entity); 117 | } 118 | } 119 | startBenchmark("update"); 120 | dao.updateInTx(list); 121 | stopBenchmark(); 122 | 123 | list = null; 124 | 125 | startBenchmark("load"); 126 | List reloaded = dao.loadAll(); 127 | stopBenchmark(); 128 | 129 | startBenchmark("access"); 130 | accessAll(reloaded); 131 | stopBenchmark(); 132 | 133 | startBenchmark("delete"); 134 | dao.deleteInTx(reloaded); 135 | stopBenchmark(); 136 | } 137 | 138 | protected void setRandomValues(SimpleEntity entity) { 139 | setRandomScalars(entity); 140 | entity.setSimpleString(randomString()); 141 | entity.setSimpleByteArray(randomBytes()); 142 | } 143 | 144 | private void setRandomScalars(SimpleEntity entity) { 145 | entity.setSimpleBoolean(random.nextBoolean()); 146 | entity.setSimpleByte((byte) random.nextInt()); 147 | entity.setSimpleShort((short) random.nextInt()); 148 | entity.setSimpleInt(random.nextInt()); 149 | entity.setSimpleLong(random.nextLong()); 150 | entity.setSimpleDouble(random.nextDouble()); 151 | entity.setSimpleFloat(random.nextFloat()); 152 | } 153 | 154 | public SimpleEntity createEntity(Long key, boolean scalarsOnly) { 155 | SimpleEntity entity = new SimpleEntity(); 156 | if (key != null) { 157 | entity.setId(key); 158 | } 159 | if (scalarsOnly) { 160 | setRandomScalars(entity); 161 | } else { 162 | setRandomValues(entity); 163 | } 164 | return entity; 165 | } 166 | 167 | protected void accessAll(List list) { 168 | for (SimpleEntity entity : list) { 169 | entity.getId(); 170 | entity.getSimpleBoolean(); 171 | entity.getSimpleByte(); 172 | entity.getSimpleShort(); 173 | entity.getSimpleInt(); 174 | entity.getSimpleLong(); 175 | entity.getSimpleFloat(); 176 | entity.getSimpleDouble(); 177 | entity.getSimpleString(); 178 | entity.getSimpleByteArray(); 179 | } 180 | } 181 | 182 | public void runBatchPerfTestIndexed() { 183 | List list = new ArrayList<>(numberEntities); 184 | for (int i = 0; i < numberEntities; i++) { 185 | list.add(createEntityIndexed((long) i)); 186 | } 187 | startBenchmark("insert"); 188 | daoIndexed.insertInTx(list); 189 | stopBenchmark(); 190 | 191 | for (SimpleEntityIndexed entity : list) { 192 | setRandomValues(entity); 193 | } 194 | startBenchmark("update"); 195 | daoIndexed.updateInTx(list); 196 | stopBenchmark(); 197 | 198 | list = null; 199 | 200 | startBenchmark("load"); 201 | List reloaded = daoIndexed.loadAll(); 202 | stopBenchmark(); 203 | 204 | startBenchmark("access"); 205 | accessAllIndexed(reloaded); 206 | stopBenchmark(); 207 | 208 | startBenchmark("delete"); 209 | daoIndexed.deleteInTx(reloaded); 210 | stopBenchmark(); 211 | } 212 | 213 | protected void setRandomValues(SimpleEntityIndexed entity) { 214 | entity.setSimpleBoolean(random.nextBoolean()); 215 | entity.setSimpleByte((byte) random.nextInt()); 216 | entity.setSimpleShort((short) random.nextInt()); 217 | entity.setSimpleInt(random.nextInt()); 218 | entity.setSimpleLong(random.nextLong()); 219 | entity.setSimpleDouble(random.nextDouble()); 220 | entity.setSimpleFloat(random.nextFloat()); 221 | entity.setSimpleString(randomString()); 222 | entity.setSimpleByteArray(randomBytes()); 223 | } 224 | 225 | public SimpleEntityIndexed createEntityIndexed(Long key) { 226 | SimpleEntityIndexed entity = new SimpleEntityIndexed(); 227 | if (key != null) { 228 | entity.setId(key); 229 | } 230 | setRandomValues(entity); 231 | return entity; 232 | } 233 | 234 | protected void accessAllIndexed(List list) { 235 | for (SimpleEntityIndexed entity : list) { 236 | entity.getId(); 237 | entity.getSimpleBoolean(); 238 | entity.getSimpleByte(); 239 | entity.getSimpleShort(); 240 | entity.getSimpleInt(); 241 | entity.getSimpleLong(); 242 | entity.getSimpleFloat(); 243 | entity.getSimpleDouble(); 244 | entity.getSimpleString(); 245 | entity.getSimpleByteArray(); 246 | } 247 | } 248 | 249 | private void runQueryByString() { 250 | if (numberEntities > 10000) { 251 | log("Reduce number of entities to 10000 to avoid extremely long test runs"); 252 | return; 253 | } 254 | List entities = new ArrayList<>(numberEntities); 255 | for (int i = 0; i < numberEntities; i++) { 256 | entities.add(createEntity((long) i, false)); 257 | } 258 | 259 | startBenchmark("insert"); 260 | dao.insertInTx(entities); 261 | stopBenchmark(); 262 | 263 | String[] stringsToLookup = new String[numberEntities]; 264 | for (int i = 0; i < numberEntities; i++) { 265 | String text = ""; 266 | while (text.length() < 2) { 267 | text = entities.get(random.nextInt(numberEntities)).getSimpleString(); 268 | } 269 | stringsToLookup[i] = text; 270 | } 271 | 272 | long entitiesFound = 0; 273 | startBenchmark("query"); 274 | Query query = dao.queryBuilder().where(SimpleEntityDao.Properties.SimpleString.eq(null)).build(); 275 | db.beginTransaction(); 276 | for (int i = 0; i < numberEntities; i++) { 277 | query.setParameter(0, stringsToLookup[i]); 278 | List result = query.list(); 279 | accessAll(result); 280 | entitiesFound += result.size(); 281 | } 282 | db.endTransaction(); 283 | stopBenchmark(); 284 | log("Entities found: " + entitiesFound); 285 | } 286 | 287 | private void runQueryByStringIndexed() { 288 | List entities = new ArrayList<>(numberEntities); 289 | for (int i = 0; i < numberEntities; i++) { 290 | entities.add(createEntityIndexed((long) i)); 291 | } 292 | 293 | startBenchmark("insert"); 294 | daoIndexed.insertInTx(entities); 295 | stopBenchmark(); 296 | 297 | String[] stringsToLookup = new String[numberEntities]; 298 | for (int i = 0; i < numberEntities; i++) { 299 | String text = ""; 300 | while (text.length() < 2) { 301 | text = entities.get(random.nextInt(numberEntities)).getSimpleString(); 302 | } 303 | stringsToLookup[i] = text; 304 | } 305 | 306 | long entitiesFound = 0; 307 | startBenchmark("query"); 308 | Query query = daoIndexed.queryBuilder().where(Properties.SimpleString.eq(null)).build(); 309 | db.beginTransaction(); 310 | for (int i = 0; i < numberEntities; i++) { 311 | query.setParameter(0, stringsToLookup[i]); 312 | List result = query.list(); 313 | accessAllIndexed(result); 314 | entitiesFound += result.size(); 315 | } 316 | db.endTransaction(); 317 | stopBenchmark(); 318 | log("Entities found: " + entitiesFound); 319 | } 320 | 321 | private void runQueryById(boolean randomIds) { 322 | List entities = new ArrayList<>(numberEntities); 323 | for (int i = 0; i < numberEntities; i++) { 324 | entities.add(createEntity((long) i, false)); 325 | } 326 | 327 | startBenchmark("insert"); 328 | dao.insertInTx(entities); 329 | stopBenchmark(); 330 | 331 | assertEntityCount(dao.count()); 332 | 333 | long[] idsToLookup = new long[numberEntities]; 334 | for (int i = 0; i < numberEntities; i++) { 335 | idsToLookup[i] = randomIds ? random.nextInt(numberEntities) : i; 336 | } 337 | 338 | startBenchmark("query"); 339 | for (int i = 0; i < numberEntities; i++) { 340 | SimpleEntity entity = dao.load(idsToLookup[i]); 341 | accessAll(entity); 342 | } 343 | stopBenchmark(); 344 | } 345 | 346 | private void accessAll(SimpleEntity entity) { 347 | entity.getId(); 348 | entity.getSimpleBoolean(); 349 | entity.getSimpleByte(); 350 | entity.getSimpleShort(); 351 | entity.getSimpleInt(); 352 | entity.getSimpleLong(); 353 | entity.getSimpleFloat(); 354 | entity.getSimpleDouble(); 355 | entity.getSimpleString(); 356 | entity.getSimpleByteArray(); 357 | } 358 | 359 | @Override 360 | public void tearDown() { 361 | daoSession.getDatabase().close(); 362 | boolean deleted = context.deleteDatabase(DB_NAME); 363 | log("DB deleted: " + deleted); 364 | } 365 | 366 | } 367 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/greendao/SimpleEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.greendao; 18 | 19 | import org.greenrobot.greendao.annotation.*; 20 | 21 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit. 22 | 23 | /** 24 | * Entity mapped to table "SIMPLE_ENTITY". 25 | */ 26 | @Entity 27 | public class SimpleEntity { 28 | 29 | @Id 30 | private long id; 31 | private boolean simpleBoolean; 32 | private byte simpleByte; 33 | private short simpleShort; 34 | private int simpleInt; 35 | private long simpleLong; 36 | private float simpleFloat; 37 | private double simpleDouble; 38 | private String simpleString; 39 | private byte[] simpleByteArray; 40 | 41 | @Generated 42 | public SimpleEntity() { 43 | } 44 | 45 | public SimpleEntity(long id) { 46 | this.id = id; 47 | } 48 | 49 | @Generated 50 | public SimpleEntity(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) { 51 | this.id = id; 52 | this.simpleBoolean = simpleBoolean; 53 | this.simpleByte = simpleByte; 54 | this.simpleShort = simpleShort; 55 | this.simpleInt = simpleInt; 56 | this.simpleLong = simpleLong; 57 | this.simpleFloat = simpleFloat; 58 | this.simpleDouble = simpleDouble; 59 | this.simpleString = simpleString; 60 | this.simpleByteArray = simpleByteArray; 61 | } 62 | 63 | public long getId() { 64 | return id; 65 | } 66 | 67 | public void setId(long id) { 68 | this.id = id; 69 | } 70 | 71 | public boolean getSimpleBoolean() { 72 | return simpleBoolean; 73 | } 74 | 75 | public void setSimpleBoolean(boolean simpleBoolean) { 76 | this.simpleBoolean = simpleBoolean; 77 | } 78 | 79 | public byte getSimpleByte() { 80 | return simpleByte; 81 | } 82 | 83 | public void setSimpleByte(byte simpleByte) { 84 | this.simpleByte = simpleByte; 85 | } 86 | 87 | public short getSimpleShort() { 88 | return simpleShort; 89 | } 90 | 91 | public void setSimpleShort(short simpleShort) { 92 | this.simpleShort = simpleShort; 93 | } 94 | 95 | public int getSimpleInt() { 96 | return simpleInt; 97 | } 98 | 99 | public void setSimpleInt(int simpleInt) { 100 | this.simpleInt = simpleInt; 101 | } 102 | 103 | public long getSimpleLong() { 104 | return simpleLong; 105 | } 106 | 107 | public void setSimpleLong(long simpleLong) { 108 | this.simpleLong = simpleLong; 109 | } 110 | 111 | public float getSimpleFloat() { 112 | return simpleFloat; 113 | } 114 | 115 | public void setSimpleFloat(float simpleFloat) { 116 | this.simpleFloat = simpleFloat; 117 | } 118 | 119 | public double getSimpleDouble() { 120 | return simpleDouble; 121 | } 122 | 123 | public void setSimpleDouble(double simpleDouble) { 124 | this.simpleDouble = simpleDouble; 125 | } 126 | 127 | public String getSimpleString() { 128 | return simpleString; 129 | } 130 | 131 | public void setSimpleString(String simpleString) { 132 | this.simpleString = simpleString; 133 | } 134 | 135 | public byte[] getSimpleByteArray() { 136 | return simpleByteArray; 137 | } 138 | 139 | public void setSimpleByteArray(byte[] simpleByteArray) { 140 | this.simpleByteArray = simpleByteArray; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/greendao/SimpleEntityDao.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.greendao; 18 | 19 | import android.database.Cursor; 20 | import android.database.sqlite.SQLiteStatement; 21 | 22 | import org.greenrobot.greendao.AbstractDao; 23 | import org.greenrobot.greendao.Property; 24 | import org.greenrobot.greendao.internal.DaoConfig; 25 | import org.greenrobot.greendao.database.Database; 26 | import org.greenrobot.greendao.database.DatabaseStatement; 27 | 28 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. 29 | /** 30 | * DAO for table "SIMPLE_ENTITY". 31 | */ 32 | public class SimpleEntityDao extends AbstractDao { 33 | 34 | public static final String TABLENAME = "SIMPLE_ENTITY"; 35 | 36 | /** 37 | * Properties of entity SimpleEntity.
38 | * Can be used for QueryBuilder and for referencing column names. 39 | */ 40 | public static class Properties { 41 | public final static Property Id = new Property(0, long.class, "id", true, "_id"); 42 | public final static Property SimpleBoolean = new Property(1, boolean.class, "simpleBoolean", false, "SIMPLE_BOOLEAN"); 43 | public final static Property SimpleByte = new Property(2, byte.class, "simpleByte", false, "SIMPLE_BYTE"); 44 | public final static Property SimpleShort = new Property(3, short.class, "simpleShort", false, "SIMPLE_SHORT"); 45 | public final static Property SimpleInt = new Property(4, int.class, "simpleInt", false, "SIMPLE_INT"); 46 | public final static Property SimpleLong = new Property(5, long.class, "simpleLong", false, "SIMPLE_LONG"); 47 | public final static Property SimpleFloat = new Property(6, float.class, "simpleFloat", false, "SIMPLE_FLOAT"); 48 | public final static Property SimpleDouble = new Property(7, double.class, "simpleDouble", false, "SIMPLE_DOUBLE"); 49 | public final static Property SimpleString = new Property(8, String.class, "simpleString", false, "SIMPLE_STRING"); 50 | public final static Property SimpleByteArray = new Property(9, byte[].class, "simpleByteArray", false, "SIMPLE_BYTE_ARRAY"); 51 | } 52 | 53 | 54 | public SimpleEntityDao(DaoConfig config) { 55 | super(config); 56 | } 57 | 58 | public SimpleEntityDao(DaoConfig config, DaoSession daoSession) { 59 | super(config, daoSession); 60 | } 61 | 62 | /** Creates the underlying database table. */ 63 | public static void createTable(Database db, boolean ifNotExists) { 64 | String constraint = ifNotExists? "IF NOT EXISTS ": ""; 65 | db.execSQL("CREATE TABLE " + constraint + "\"SIMPLE_ENTITY\" (" + // 66 | "\"_id\" INTEGER PRIMARY KEY NOT NULL ," + // 0: id 67 | "\"SIMPLE_BOOLEAN\" INTEGER NOT NULL ," + // 1: simpleBoolean 68 | "\"SIMPLE_BYTE\" INTEGER NOT NULL ," + // 2: simpleByte 69 | "\"SIMPLE_SHORT\" INTEGER NOT NULL ," + // 3: simpleShort 70 | "\"SIMPLE_INT\" INTEGER NOT NULL ," + // 4: simpleInt 71 | "\"SIMPLE_LONG\" INTEGER NOT NULL ," + // 5: simpleLong 72 | "\"SIMPLE_FLOAT\" REAL NOT NULL ," + // 6: simpleFloat 73 | "\"SIMPLE_DOUBLE\" REAL NOT NULL ," + // 7: simpleDouble 74 | "\"SIMPLE_STRING\" TEXT," + // 8: simpleString 75 | "\"SIMPLE_BYTE_ARRAY\" BLOB);"); // 9: simpleByteArray 76 | } 77 | 78 | /** Drops the underlying database table. */ 79 | public static void dropTable(Database db, boolean ifExists) { 80 | String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"SIMPLE_ENTITY\""; 81 | db.execSQL(sql); 82 | } 83 | 84 | @Override 85 | protected final void bindValues(DatabaseStatement stmt, SimpleEntity entity) { 86 | stmt.clearBindings(); 87 | stmt.bindLong(1, entity.getId()); 88 | stmt.bindLong(2, entity.getSimpleBoolean() ? 1L: 0L); 89 | stmt.bindLong(3, entity.getSimpleByte()); 90 | stmt.bindLong(4, entity.getSimpleShort()); 91 | stmt.bindLong(5, entity.getSimpleInt()); 92 | stmt.bindLong(6, entity.getSimpleLong()); 93 | stmt.bindDouble(7, entity.getSimpleFloat()); 94 | stmt.bindDouble(8, entity.getSimpleDouble()); 95 | 96 | String simpleString = entity.getSimpleString(); 97 | if (simpleString != null) { 98 | stmt.bindString(9, simpleString); 99 | } 100 | 101 | byte[] simpleByteArray = entity.getSimpleByteArray(); 102 | if (simpleByteArray != null) { 103 | stmt.bindBlob(10, simpleByteArray); 104 | } 105 | } 106 | 107 | @Override 108 | protected final void bindValues(SQLiteStatement stmt, SimpleEntity entity) { 109 | stmt.clearBindings(); 110 | stmt.bindLong(1, entity.getId()); 111 | stmt.bindLong(2, entity.getSimpleBoolean() ? 1L: 0L); 112 | stmt.bindLong(3, entity.getSimpleByte()); 113 | stmt.bindLong(4, entity.getSimpleShort()); 114 | stmt.bindLong(5, entity.getSimpleInt()); 115 | stmt.bindLong(6, entity.getSimpleLong()); 116 | stmt.bindDouble(7, entity.getSimpleFloat()); 117 | stmt.bindDouble(8, entity.getSimpleDouble()); 118 | 119 | String simpleString = entity.getSimpleString(); 120 | if (simpleString != null) { 121 | stmt.bindString(9, simpleString); 122 | } 123 | 124 | byte[] simpleByteArray = entity.getSimpleByteArray(); 125 | if (simpleByteArray != null) { 126 | stmt.bindBlob(10, simpleByteArray); 127 | } 128 | } 129 | 130 | @Override 131 | public Long readKey(Cursor cursor, int offset) { 132 | return cursor.getLong(offset + 0); 133 | } 134 | 135 | @Override 136 | public SimpleEntity readEntity(Cursor cursor, int offset) { 137 | SimpleEntity entity = new SimpleEntity( // 138 | cursor.getLong(offset + 0), // id 139 | cursor.getShort(offset + 1) != 0, // simpleBoolean 140 | (byte) cursor.getShort(offset + 2), // simpleByte 141 | cursor.getShort(offset + 3), // simpleShort 142 | cursor.getInt(offset + 4), // simpleInt 143 | cursor.getLong(offset + 5), // simpleLong 144 | cursor.getFloat(offset + 6), // simpleFloat 145 | cursor.getDouble(offset + 7), // simpleDouble 146 | cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8), // simpleString 147 | cursor.isNull(offset + 9) ? null : cursor.getBlob(offset + 9) // simpleByteArray 148 | ); 149 | return entity; 150 | } 151 | 152 | @Override 153 | public void readEntity(Cursor cursor, SimpleEntity entity, int offset) { 154 | entity.setId(cursor.getLong(offset + 0)); 155 | entity.setSimpleBoolean(cursor.getShort(offset + 1) != 0); 156 | entity.setSimpleByte((byte) cursor.getShort(offset + 2)); 157 | entity.setSimpleShort(cursor.getShort(offset + 3)); 158 | entity.setSimpleInt(cursor.getInt(offset + 4)); 159 | entity.setSimpleLong(cursor.getLong(offset + 5)); 160 | entity.setSimpleFloat(cursor.getFloat(offset + 6)); 161 | entity.setSimpleDouble(cursor.getDouble(offset + 7)); 162 | entity.setSimpleString(cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8)); 163 | entity.setSimpleByteArray(cursor.isNull(offset + 9) ? null : cursor.getBlob(offset + 9)); 164 | } 165 | 166 | @Override 167 | protected final Long updateKeyAfterInsert(SimpleEntity entity, long rowId) { 168 | entity.setId(rowId); 169 | return rowId; 170 | } 171 | 172 | @Override 173 | public Long getKey(SimpleEntity entity) { 174 | if(entity != null) { 175 | return entity.getId(); 176 | } else { 177 | return null; 178 | } 179 | } 180 | 181 | @Override 182 | public boolean hasKey(SimpleEntity entity) { 183 | throw new UnsupportedOperationException("Unsupported for entities with a non-null key"); 184 | } 185 | 186 | @Override 187 | protected final boolean isEntityUpdateable() { 188 | return true; 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/greendao/SimpleEntityIndexed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.greendao; 18 | 19 | import org.greenrobot.greendao.annotation.*; 20 | 21 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit. 22 | 23 | /** 24 | * Entity mapped to table "SIMPLE_ENTITY_INDEXED". 25 | */ 26 | @Entity 27 | public class SimpleEntityIndexed { 28 | 29 | @Id 30 | private long id; 31 | private boolean simpleBoolean; 32 | private byte simpleByte; 33 | private short simpleShort; 34 | 35 | @Index 36 | private int simpleInt; 37 | private long simpleLong; 38 | private float simpleFloat; 39 | private double simpleDouble; 40 | 41 | @Index 42 | private String simpleString; 43 | private byte[] simpleByteArray; 44 | 45 | @Generated 46 | public SimpleEntityIndexed() { 47 | } 48 | 49 | public SimpleEntityIndexed(long id) { 50 | this.id = id; 51 | } 52 | 53 | @Generated 54 | public SimpleEntityIndexed(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) { 55 | this.id = id; 56 | this.simpleBoolean = simpleBoolean; 57 | this.simpleByte = simpleByte; 58 | this.simpleShort = simpleShort; 59 | this.simpleInt = simpleInt; 60 | this.simpleLong = simpleLong; 61 | this.simpleFloat = simpleFloat; 62 | this.simpleDouble = simpleDouble; 63 | this.simpleString = simpleString; 64 | this.simpleByteArray = simpleByteArray; 65 | } 66 | 67 | public long getId() { 68 | return id; 69 | } 70 | 71 | public void setId(long id) { 72 | this.id = id; 73 | } 74 | 75 | public boolean getSimpleBoolean() { 76 | return simpleBoolean; 77 | } 78 | 79 | public void setSimpleBoolean(boolean simpleBoolean) { 80 | this.simpleBoolean = simpleBoolean; 81 | } 82 | 83 | public byte getSimpleByte() { 84 | return simpleByte; 85 | } 86 | 87 | public void setSimpleByte(byte simpleByte) { 88 | this.simpleByte = simpleByte; 89 | } 90 | 91 | public short getSimpleShort() { 92 | return simpleShort; 93 | } 94 | 95 | public void setSimpleShort(short simpleShort) { 96 | this.simpleShort = simpleShort; 97 | } 98 | 99 | public int getSimpleInt() { 100 | return simpleInt; 101 | } 102 | 103 | public void setSimpleInt(int simpleInt) { 104 | this.simpleInt = simpleInt; 105 | } 106 | 107 | public long getSimpleLong() { 108 | return simpleLong; 109 | } 110 | 111 | public void setSimpleLong(long simpleLong) { 112 | this.simpleLong = simpleLong; 113 | } 114 | 115 | public float getSimpleFloat() { 116 | return simpleFloat; 117 | } 118 | 119 | public void setSimpleFloat(float simpleFloat) { 120 | this.simpleFloat = simpleFloat; 121 | } 122 | 123 | public double getSimpleDouble() { 124 | return simpleDouble; 125 | } 126 | 127 | public void setSimpleDouble(double simpleDouble) { 128 | this.simpleDouble = simpleDouble; 129 | } 130 | 131 | public String getSimpleString() { 132 | return simpleString; 133 | } 134 | 135 | public void setSimpleString(String simpleString) { 136 | this.simpleString = simpleString; 137 | } 138 | 139 | public byte[] getSimpleByteArray() { 140 | return simpleByteArray; 141 | } 142 | 143 | public void setSimpleByteArray(byte[] simpleByteArray) { 144 | this.simpleByteArray = simpleByteArray; 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/greendao/SimpleEntityIndexedDao.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.greendao; 18 | 19 | import android.database.Cursor; 20 | import android.database.sqlite.SQLiteStatement; 21 | 22 | import org.greenrobot.greendao.AbstractDao; 23 | import org.greenrobot.greendao.Property; 24 | import org.greenrobot.greendao.internal.DaoConfig; 25 | import org.greenrobot.greendao.database.Database; 26 | import org.greenrobot.greendao.database.DatabaseStatement; 27 | 28 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. 29 | /** 30 | * DAO for table "SIMPLE_ENTITY_INDEXED". 31 | */ 32 | public class SimpleEntityIndexedDao extends AbstractDao { 33 | 34 | public static final String TABLENAME = "SIMPLE_ENTITY_INDEXED"; 35 | 36 | /** 37 | * Properties of entity SimpleEntityIndexed.
38 | * Can be used for QueryBuilder and for referencing column names. 39 | */ 40 | public static class Properties { 41 | public final static Property Id = new Property(0, long.class, "id", true, "_id"); 42 | public final static Property SimpleBoolean = new Property(1, boolean.class, "simpleBoolean", false, "SIMPLE_BOOLEAN"); 43 | public final static Property SimpleByte = new Property(2, byte.class, "simpleByte", false, "SIMPLE_BYTE"); 44 | public final static Property SimpleShort = new Property(3, short.class, "simpleShort", false, "SIMPLE_SHORT"); 45 | public final static Property SimpleInt = new Property(4, int.class, "simpleInt", false, "SIMPLE_INT"); 46 | public final static Property SimpleLong = new Property(5, long.class, "simpleLong", false, "SIMPLE_LONG"); 47 | public final static Property SimpleFloat = new Property(6, float.class, "simpleFloat", false, "SIMPLE_FLOAT"); 48 | public final static Property SimpleDouble = new Property(7, double.class, "simpleDouble", false, "SIMPLE_DOUBLE"); 49 | public final static Property SimpleString = new Property(8, String.class, "simpleString", false, "SIMPLE_STRING"); 50 | public final static Property SimpleByteArray = new Property(9, byte[].class, "simpleByteArray", false, "SIMPLE_BYTE_ARRAY"); 51 | } 52 | 53 | 54 | public SimpleEntityIndexedDao(DaoConfig config) { 55 | super(config); 56 | } 57 | 58 | public SimpleEntityIndexedDao(DaoConfig config, DaoSession daoSession) { 59 | super(config, daoSession); 60 | } 61 | 62 | /** Creates the underlying database table. */ 63 | public static void createTable(Database db, boolean ifNotExists) { 64 | String constraint = ifNotExists? "IF NOT EXISTS ": ""; 65 | db.execSQL("CREATE TABLE " + constraint + "\"SIMPLE_ENTITY_INDEXED\" (" + // 66 | "\"_id\" INTEGER PRIMARY KEY NOT NULL ," + // 0: id 67 | "\"SIMPLE_BOOLEAN\" INTEGER NOT NULL ," + // 1: simpleBoolean 68 | "\"SIMPLE_BYTE\" INTEGER NOT NULL ," + // 2: simpleByte 69 | "\"SIMPLE_SHORT\" INTEGER NOT NULL ," + // 3: simpleShort 70 | "\"SIMPLE_INT\" INTEGER NOT NULL ," + // 4: simpleInt 71 | "\"SIMPLE_LONG\" INTEGER NOT NULL ," + // 5: simpleLong 72 | "\"SIMPLE_FLOAT\" REAL NOT NULL ," + // 6: simpleFloat 73 | "\"SIMPLE_DOUBLE\" REAL NOT NULL ," + // 7: simpleDouble 74 | "\"SIMPLE_STRING\" TEXT," + // 8: simpleString 75 | "\"SIMPLE_BYTE_ARRAY\" BLOB);"); // 9: simpleByteArray 76 | // Add Indexes 77 | db.execSQL("CREATE INDEX " + constraint + "IDX_SIMPLE_ENTITY_INDEXED_SIMPLE_INT ON \"SIMPLE_ENTITY_INDEXED\"" + 78 | " (\"SIMPLE_INT\");"); 79 | db.execSQL("CREATE INDEX " + constraint + "IDX_SIMPLE_ENTITY_INDEXED_SIMPLE_STRING ON \"SIMPLE_ENTITY_INDEXED\"" + 80 | " (\"SIMPLE_STRING\");"); 81 | } 82 | 83 | /** Drops the underlying database table. */ 84 | public static void dropTable(Database db, boolean ifExists) { 85 | String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"SIMPLE_ENTITY_INDEXED\""; 86 | db.execSQL(sql); 87 | } 88 | 89 | @Override 90 | protected final void bindValues(DatabaseStatement stmt, SimpleEntityIndexed entity) { 91 | stmt.clearBindings(); 92 | stmt.bindLong(1, entity.getId()); 93 | stmt.bindLong(2, entity.getSimpleBoolean() ? 1L: 0L); 94 | stmt.bindLong(3, entity.getSimpleByte()); 95 | stmt.bindLong(4, entity.getSimpleShort()); 96 | stmt.bindLong(5, entity.getSimpleInt()); 97 | stmt.bindLong(6, entity.getSimpleLong()); 98 | stmt.bindDouble(7, entity.getSimpleFloat()); 99 | stmt.bindDouble(8, entity.getSimpleDouble()); 100 | 101 | String simpleString = entity.getSimpleString(); 102 | if (simpleString != null) { 103 | stmt.bindString(9, simpleString); 104 | } 105 | 106 | byte[] simpleByteArray = entity.getSimpleByteArray(); 107 | if (simpleByteArray != null) { 108 | stmt.bindBlob(10, simpleByteArray); 109 | } 110 | } 111 | 112 | @Override 113 | protected final void bindValues(SQLiteStatement stmt, SimpleEntityIndexed entity) { 114 | stmt.clearBindings(); 115 | stmt.bindLong(1, entity.getId()); 116 | stmt.bindLong(2, entity.getSimpleBoolean() ? 1L: 0L); 117 | stmt.bindLong(3, entity.getSimpleByte()); 118 | stmt.bindLong(4, entity.getSimpleShort()); 119 | stmt.bindLong(5, entity.getSimpleInt()); 120 | stmt.bindLong(6, entity.getSimpleLong()); 121 | stmt.bindDouble(7, entity.getSimpleFloat()); 122 | stmt.bindDouble(8, entity.getSimpleDouble()); 123 | 124 | String simpleString = entity.getSimpleString(); 125 | if (simpleString != null) { 126 | stmt.bindString(9, simpleString); 127 | } 128 | 129 | byte[] simpleByteArray = entity.getSimpleByteArray(); 130 | if (simpleByteArray != null) { 131 | stmt.bindBlob(10, simpleByteArray); 132 | } 133 | } 134 | 135 | @Override 136 | public Long readKey(Cursor cursor, int offset) { 137 | return cursor.getLong(offset + 0); 138 | } 139 | 140 | @Override 141 | public SimpleEntityIndexed readEntity(Cursor cursor, int offset) { 142 | SimpleEntityIndexed entity = new SimpleEntityIndexed( // 143 | cursor.getLong(offset + 0), // id 144 | cursor.getShort(offset + 1) != 0, // simpleBoolean 145 | (byte) cursor.getShort(offset + 2), // simpleByte 146 | cursor.getShort(offset + 3), // simpleShort 147 | cursor.getInt(offset + 4), // simpleInt 148 | cursor.getLong(offset + 5), // simpleLong 149 | cursor.getFloat(offset + 6), // simpleFloat 150 | cursor.getDouble(offset + 7), // simpleDouble 151 | cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8), // simpleString 152 | cursor.isNull(offset + 9) ? null : cursor.getBlob(offset + 9) // simpleByteArray 153 | ); 154 | return entity; 155 | } 156 | 157 | @Override 158 | public void readEntity(Cursor cursor, SimpleEntityIndexed entity, int offset) { 159 | entity.setId(cursor.getLong(offset + 0)); 160 | entity.setSimpleBoolean(cursor.getShort(offset + 1) != 0); 161 | entity.setSimpleByte((byte) cursor.getShort(offset + 2)); 162 | entity.setSimpleShort(cursor.getShort(offset + 3)); 163 | entity.setSimpleInt(cursor.getInt(offset + 4)); 164 | entity.setSimpleLong(cursor.getLong(offset + 5)); 165 | entity.setSimpleFloat(cursor.getFloat(offset + 6)); 166 | entity.setSimpleDouble(cursor.getDouble(offset + 7)); 167 | entity.setSimpleString(cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8)); 168 | entity.setSimpleByteArray(cursor.isNull(offset + 9) ? null : cursor.getBlob(offset + 9)); 169 | } 170 | 171 | @Override 172 | protected final Long updateKeyAfterInsert(SimpleEntityIndexed entity, long rowId) { 173 | entity.setId(rowId); 174 | return rowId; 175 | } 176 | 177 | @Override 178 | public Long getKey(SimpleEntityIndexed entity) { 179 | if(entity != null) { 180 | return entity.getId(); 181 | } else { 182 | return null; 183 | } 184 | } 185 | 186 | @Override 187 | public boolean hasKey(SimpleEntityIndexed entity) { 188 | throw new UnsupportedOperationException("Unsupported for entities with a non-null key"); 189 | } 190 | 191 | @Override 192 | protected final boolean isEntityUpdateable() { 193 | return true; 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/objectbox/ObjectBoxPerfTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2024 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.objectbox; 18 | 19 | import android.content.Context; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import io.objectbox.Box; 25 | import io.objectbox.BoxStore; 26 | import io.objectbox.performanceapp.PerfTest; 27 | import io.objectbox.performanceapp.PerfTestRunner; 28 | import io.objectbox.performanceapp.TestType; 29 | import io.objectbox.query.Query; 30 | 31 | import static io.objectbox.query.QueryBuilder.StringOrder.CASE_SENSITIVE; 32 | 33 | public class ObjectBoxPerfTest extends PerfTest { 34 | private BoxStore store; 35 | 36 | private boolean versionLoggedOnce; 37 | private Box box; 38 | private Box boxIndexed; 39 | 40 | @Override 41 | public String name() { 42 | return "ObjectBox"; 43 | } 44 | 45 | public void setUp(Context context, PerfTestRunner testRunner) { 46 | super.setUp(context, testRunner); 47 | store = MyObjectBox.builder().androidContext(context).build(); 48 | store.close(); 49 | store.deleteAllFiles(); 50 | // 2 GB for DB to allow putting millions of objects 51 | store = MyObjectBox.builder().androidContext(context).maxSizeInKByte(2 * 1024 * 1024).build(); 52 | box = store.boxFor(SimpleEntity.class); 53 | boxIndexed = store.boxFor(SimpleEntityIndexed.class); 54 | 55 | if (!versionLoggedOnce) { 56 | String versionNative = BoxStore.getVersionNative(); 57 | String versionJava = BoxStore.getVersion(); 58 | if (versionJava.equals(versionNative)) { 59 | log("ObjectBox " + versionNative); 60 | } else { 61 | log("ObjectBox " + versionNative + " (Java: " + versionJava + ")"); 62 | } 63 | versionLoggedOnce = true; 64 | } 65 | } 66 | 67 | @Override 68 | public void run(TestType type) { 69 | switch (type.name) { 70 | case TestType.CRUD: 71 | runBatchPerfTest(false); 72 | break; 73 | case TestType.CRUD_SCALARS: 74 | runBatchPerfTest(true); 75 | break; 76 | case TestType.CRUD_INDEXED: 77 | runBatchPerfTestIndexed(); 78 | break; 79 | case TestType.QUERY_STRING: 80 | runQueryByString(); 81 | break; 82 | case TestType.QUERY_STRING_INDEXED: 83 | runQueryByStringIndexed(); 84 | break; 85 | case TestType.QUERY_INTEGER: 86 | runQueryByInteger(); 87 | break; 88 | case TestType.QUERY_INTEGER_INDEXED: 89 | runQueryByIntegerIndexed(); 90 | break; 91 | case TestType.QUERY_ID: 92 | runQueryById(false); 93 | break; 94 | case TestType.QUERY_ID_RANDOM: 95 | runQueryById(true); 96 | break; 97 | } 98 | } 99 | 100 | public void runBatchPerfTest(boolean scalarsOnly) { 101 | List list = prepareAndPutEntities(scalarsOnly); 102 | 103 | for (SimpleEntity entity : list) { 104 | if (scalarsOnly) { 105 | setRandomScalars(entity); 106 | } else { 107 | setRandomValues(entity); 108 | } 109 | } 110 | startBenchmark("update"); 111 | box.put(list); 112 | stopBenchmark(); 113 | 114 | //noinspection UnusedAssignment 115 | list = null; 116 | 117 | startBenchmark("load"); 118 | List reloaded = box.getAll(); 119 | stopBenchmark(); 120 | 121 | assertEntityCount(reloaded.size()); 122 | 123 | startBenchmark("access"); 124 | accessAll(reloaded); 125 | stopBenchmark(); 126 | 127 | startBenchmark("delete"); 128 | box.remove(reloaded); 129 | stopBenchmark(); 130 | } 131 | 132 | protected void setRandomValues(SimpleEntity entity) { 133 | setRandomScalars(entity); 134 | entity.setSimpleString(randomString()); 135 | entity.setSimpleByteArray(randomBytes()); 136 | } 137 | 138 | private void setRandomScalars(SimpleEntity entity) { 139 | entity.setSimpleBoolean(random.nextBoolean()); 140 | entity.setSimpleByte((byte) random.nextInt()); 141 | entity.setSimpleShort((short) random.nextInt()); 142 | entity.setSimpleInt(random.nextInt()); 143 | entity.setSimpleLong(random.nextLong()); 144 | entity.setSimpleDouble(random.nextDouble()); 145 | entity.setSimpleFloat(random.nextFloat()); 146 | } 147 | 148 | public SimpleEntity createEntity(boolean scalarsOnly) { 149 | SimpleEntity entity = new SimpleEntity(); 150 | if (scalarsOnly) { 151 | setRandomScalars(entity); 152 | } else { 153 | setRandomValues(entity); 154 | } 155 | return entity; 156 | } 157 | 158 | @SuppressWarnings("ResultOfMethodCallIgnored") 159 | protected void accessAll(List list) { 160 | for (SimpleEntity entity : list) { 161 | entity.getId(); 162 | entity.getSimpleBoolean(); 163 | entity.getSimpleByte(); 164 | entity.getSimpleShort(); 165 | entity.getSimpleInt(); 166 | entity.getSimpleLong(); 167 | entity.getSimpleFloat(); 168 | entity.getSimpleDouble(); 169 | entity.getSimpleString(); 170 | entity.getSimpleByteArray(); 171 | } 172 | } 173 | 174 | public void runBatchPerfTestIndexed() { 175 | List list = prepareAndPutEntitiesIndexed(); 176 | 177 | for (SimpleEntityIndexed entity : list) { 178 | setRandomValues(entity); 179 | } 180 | startBenchmark("update"); 181 | boxIndexed.put(list); 182 | stopBenchmark(); 183 | 184 | //noinspection UnusedAssignment 185 | list = null; 186 | 187 | startBenchmark("load"); 188 | List reloaded = boxIndexed.getAll(); 189 | stopBenchmark(); 190 | 191 | startBenchmark("access"); 192 | accessAllIndexed(reloaded); 193 | stopBenchmark(); 194 | 195 | startBenchmark("delete"); 196 | boxIndexed.remove(reloaded); 197 | stopBenchmark(); 198 | } 199 | 200 | protected void setRandomValues(SimpleEntityIndexed entity) { 201 | entity.setSimpleBoolean(random.nextBoolean()); 202 | entity.setSimpleByte((byte) random.nextInt()); 203 | entity.setSimpleShort((short) random.nextInt()); 204 | entity.setSimpleInt(random.nextInt()); 205 | entity.setSimpleLong(random.nextLong()); 206 | entity.setSimpleDouble(random.nextDouble()); 207 | entity.setSimpleFloat(random.nextFloat()); 208 | entity.setSimpleString(randomString()); 209 | entity.setSimpleByteArray(randomBytes()); 210 | } 211 | 212 | public SimpleEntityIndexed createEntityIndexed() { 213 | SimpleEntityIndexed entity = new SimpleEntityIndexed(); 214 | setRandomValues(entity); 215 | return entity; 216 | } 217 | 218 | @SuppressWarnings("ResultOfMethodCallIgnored") 219 | protected void accessAllIndexed(List list) { 220 | for (SimpleEntityIndexed entity : list) { 221 | entity.getId(); 222 | entity.getSimpleBoolean(); 223 | entity.getSimpleByte(); 224 | entity.getSimpleShort(); 225 | entity.getSimpleInt(); 226 | entity.getSimpleLong(); 227 | entity.getSimpleFloat(); 228 | entity.getSimpleDouble(); 229 | entity.getSimpleString(); 230 | entity.getSimpleByteArray(); 231 | } 232 | } 233 | 234 | private void runQueryByString() { 235 | if (numberEntities > 10000) { 236 | log("Reduce number of entities to 10000 to avoid extremely long test runs"); 237 | return; 238 | } 239 | List entities = prepareAndPutEntities(false); 240 | 241 | final String[] stringsToLookup = new String[numberEntities]; 242 | for (int i = 0; i < stringsToLookup.length; i++) { 243 | String text = ""; 244 | while (text.length() < 2) { 245 | text = entities.get(random.nextInt(numberEntities)).getSimpleString(); 246 | } 247 | stringsToLookup[i] = text; 248 | } 249 | 250 | startBenchmark("query"); 251 | 252 | long entitiesFound = 0; 253 | try (Query query = box.query( 254 | SimpleEntity_.simpleString 255 | .equal("", CASE_SENSITIVE) 256 | .alias("string") 257 | ).build()) { 258 | for (int i = 0; i < stringsToLookup.length; i++) { 259 | query.setParameter("string", stringsToLookup[i]); 260 | List result = query.find(); 261 | accessAll(result); 262 | entitiesFound += result.size(); 263 | } 264 | } 265 | stopBenchmark(); 266 | log("Entities found: " + entitiesFound); 267 | } 268 | 269 | private void runQueryByInteger() { 270 | if (numberEntities > 10000) { 271 | log("Reduce number of entities to 10000 to avoid extremely long test runs"); 272 | return; 273 | } 274 | List entities = prepareAndPutEntities(false); 275 | final int[] valuesToLookup = new int[numberEntities]; 276 | for (int i = 0; i < numberEntities; i++) { 277 | valuesToLookup[i] = entities.get(random.nextInt(numberEntities)).getSimpleInt(); 278 | } 279 | 280 | startBenchmark("query"); 281 | long entitiesFound = 0; 282 | try (Query query = box.query( 283 | SimpleEntity_.simpleInt 284 | .equal(0) 285 | .alias("int") 286 | ).build()) { 287 | for (int i = 0; i < numberEntities; i++) { 288 | query.setParameter("int", valuesToLookup[i]); 289 | List result = query.find(); 290 | accessAll(result); 291 | entitiesFound += result.size(); 292 | } 293 | } 294 | stopBenchmark(); 295 | log("Entities found: " + entitiesFound); 296 | assertGreaterOrEqualToNumberOfEntities(entitiesFound); 297 | } 298 | 299 | private List prepareAndPutEntities(boolean scalarsOnly) { 300 | List entities = new ArrayList<>(numberEntities); 301 | for (int i = 0; i < numberEntities; i++) { 302 | entities.add(createEntity(scalarsOnly)); 303 | } 304 | log("Prepared test data: " + numberEntities + " objects"); 305 | 306 | startBenchmark("insert"); 307 | box.put(entities); 308 | stopBenchmark(); 309 | 310 | assertEntityCount(box.count()); 311 | return entities; 312 | } 313 | 314 | private void runQueryByStringIndexed() { 315 | List entities = prepareAndPutEntitiesIndexed(); 316 | 317 | final String[] stringsToLookup = new String[numberEntities]; 318 | for (int i = 0; i < stringsToLookup.length; i++) { 319 | String text = ""; 320 | while (text.length() < 2) { 321 | text = entities.get(random.nextInt(numberEntities)).getSimpleString(); 322 | } 323 | stringsToLookup[i] = text; 324 | } 325 | 326 | startBenchmark("query"); 327 | long entitiesFound = 0; 328 | try (Query query = boxIndexed.query( 329 | SimpleEntityIndexed_.simpleString 330 | .equal("", CASE_SENSITIVE) 331 | .alias("string") 332 | ).build()) { 333 | for (int i = 0; i < stringsToLookup.length; i++) { 334 | query.setParameter("string", stringsToLookup[i]); 335 | List result = query.find(); 336 | accessAllIndexed(result); 337 | entitiesFound += result.size(); 338 | } 339 | } 340 | stopBenchmark(); 341 | log("Entities found: " + entitiesFound); 342 | assertGreaterOrEqualToNumberOfEntities(entitiesFound); 343 | } 344 | 345 | private void runQueryByIntegerIndexed() { 346 | List entities = prepareAndPutEntitiesIndexed(); 347 | final int[] valuesToLookup = new int[numberEntities]; 348 | for (int i = 0; i < numberEntities; i++) { 349 | valuesToLookup[i] = entities.get(random.nextInt(numberEntities)).getSimpleInt(); 350 | } 351 | 352 | startBenchmark("query"); 353 | long entitiesFound = 0; 354 | try (Query query = boxIndexed.query( 355 | SimpleEntityIndexed_.simpleInt 356 | .equal(0) 357 | .alias("int") 358 | ).build()) { 359 | for (int i = 0; i < numberEntities; i++) { 360 | query.setParameter("int", valuesToLookup[i]); 361 | List result = query.find(); 362 | accessAllIndexed(result); 363 | entitiesFound += result.size(); 364 | } 365 | } 366 | stopBenchmark(); 367 | log("Entities found: " + entitiesFound); 368 | assertGreaterOrEqualToNumberOfEntities(entitiesFound); 369 | } 370 | 371 | private List prepareAndPutEntitiesIndexed() { 372 | List entities = new ArrayList<>(numberEntities); 373 | for (int i = 0; i < numberEntities; i++) { 374 | entities.add(createEntityIndexed()); 375 | } 376 | 377 | startBenchmark("insert"); 378 | boxIndexed.put(entities); 379 | stopBenchmark(); 380 | 381 | assertEntityCount(boxIndexed.count()); 382 | 383 | return entities; 384 | } 385 | 386 | private void runQueryById(boolean randomIds) { 387 | prepareAndPutEntities(false); 388 | 389 | final long[] idsToLookup = new long[numberEntities]; 390 | for (int i = 0; i < numberEntities; i++) { 391 | idsToLookup[i] = randomIds ? 1 + random.nextInt(numberEntities) : 1 + i; 392 | } 393 | 394 | benchmark("query", () -> { 395 | List results = box.get(idsToLookup); 396 | if (results.size() != idsToLookup.length) { 397 | throw new IllegalStateException("result count " + results.size() 398 | + " is not " + idsToLookup.length); 399 | } 400 | accessAll(results); 401 | }); 402 | } 403 | 404 | @Override 405 | public void tearDown() { 406 | store.close(); 407 | store.deleteAllFiles(); 408 | } 409 | 410 | } 411 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/objectbox/SimpleEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.objectbox; 18 | 19 | import io.objectbox.annotation.Entity; 20 | import io.objectbox.annotation.Id; 21 | 22 | @Entity 23 | public class SimpleEntity { 24 | 25 | @Id 26 | long id; 27 | boolean simpleBoolean; 28 | byte simpleByte; 29 | short simpleShort; 30 | int simpleInt; 31 | long simpleLong; 32 | float simpleFloat; 33 | double simpleDouble; 34 | 35 | String simpleString; 36 | 37 | byte[] simpleByteArray; 38 | 39 | public SimpleEntity() { 40 | } 41 | 42 | public SimpleEntity(long id) { 43 | this.id = id; 44 | } 45 | 46 | public SimpleEntity(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) { 47 | this.id = id; 48 | this.simpleBoolean = simpleBoolean; 49 | this.simpleByte = simpleByte; 50 | this.simpleShort = simpleShort; 51 | this.simpleInt = simpleInt; 52 | this.simpleLong = simpleLong; 53 | this.simpleFloat = simpleFloat; 54 | this.simpleDouble = simpleDouble; 55 | this.simpleString = simpleString; 56 | this.simpleByteArray = simpleByteArray; 57 | } 58 | 59 | public long getId() { 60 | return id; 61 | } 62 | 63 | public void setId(long id) { 64 | this.id = id; 65 | } 66 | 67 | public boolean getSimpleBoolean() { 68 | return simpleBoolean; 69 | } 70 | 71 | public void setSimpleBoolean(boolean simpleBoolean) { 72 | this.simpleBoolean = simpleBoolean; 73 | } 74 | 75 | public byte getSimpleByte() { 76 | return simpleByte; 77 | } 78 | 79 | public void setSimpleByte(byte simpleByte) { 80 | this.simpleByte = simpleByte; 81 | } 82 | 83 | public short getSimpleShort() { 84 | return simpleShort; 85 | } 86 | 87 | public void setSimpleShort(short simpleShort) { 88 | this.simpleShort = simpleShort; 89 | } 90 | 91 | public int getSimpleInt() { 92 | return simpleInt; 93 | } 94 | 95 | public void setSimpleInt(int simpleInt) { 96 | this.simpleInt = simpleInt; 97 | } 98 | 99 | public long getSimpleLong() { 100 | return simpleLong; 101 | } 102 | 103 | public void setSimpleLong(long simpleLong) { 104 | this.simpleLong = simpleLong; 105 | } 106 | 107 | public float getSimpleFloat() { 108 | return simpleFloat; 109 | } 110 | 111 | public void setSimpleFloat(float simpleFloat) { 112 | this.simpleFloat = simpleFloat; 113 | } 114 | 115 | public double getSimpleDouble() { 116 | return simpleDouble; 117 | } 118 | 119 | public void setSimpleDouble(double simpleDouble) { 120 | this.simpleDouble = simpleDouble; 121 | } 122 | 123 | public String getSimpleString() { 124 | return simpleString; 125 | } 126 | 127 | public void setSimpleString(String simpleString) { 128 | this.simpleString = simpleString; 129 | } 130 | 131 | public byte[] getSimpleByteArray() { 132 | return simpleByteArray; 133 | } 134 | 135 | public void setSimpleByteArray(byte[] simpleByteArray) { 136 | this.simpleByteArray = simpleByteArray; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/objectbox/SimpleEntityIndexed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.objectbox; 18 | 19 | import io.objectbox.annotation.Entity; 20 | import io.objectbox.annotation.Id; 21 | import io.objectbox.annotation.Index; 22 | 23 | @Entity 24 | public class SimpleEntityIndexed { 25 | 26 | @Id 27 | private long id; 28 | private boolean simpleBoolean; 29 | private byte simpleByte; 30 | private short simpleShort; 31 | @Index 32 | private int simpleInt; 33 | private long simpleLong; 34 | private float simpleFloat; 35 | private double simpleDouble; 36 | 37 | @Index 38 | private String simpleString; 39 | 40 | private byte[] simpleByteArray; 41 | 42 | public SimpleEntityIndexed() { 43 | } 44 | 45 | public SimpleEntityIndexed(long id) { 46 | this.id = id; 47 | } 48 | 49 | public SimpleEntityIndexed(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) { 50 | this.id = id; 51 | this.simpleBoolean = simpleBoolean; 52 | this.simpleByte = simpleByte; 53 | this.simpleShort = simpleShort; 54 | this.simpleInt = simpleInt; 55 | this.simpleLong = simpleLong; 56 | this.simpleFloat = simpleFloat; 57 | this.simpleDouble = simpleDouble; 58 | this.simpleString = simpleString; 59 | this.simpleByteArray = simpleByteArray; 60 | } 61 | 62 | public long getId() { 63 | return id; 64 | } 65 | 66 | public void setId(long id) { 67 | this.id = id; 68 | } 69 | 70 | public boolean getSimpleBoolean() { 71 | return simpleBoolean; 72 | } 73 | 74 | public void setSimpleBoolean(boolean simpleBoolean) { 75 | this.simpleBoolean = simpleBoolean; 76 | } 77 | 78 | public byte getSimpleByte() { 79 | return simpleByte; 80 | } 81 | 82 | public void setSimpleByte(byte simpleByte) { 83 | this.simpleByte = simpleByte; 84 | } 85 | 86 | public short getSimpleShort() { 87 | return simpleShort; 88 | } 89 | 90 | public void setSimpleShort(short simpleShort) { 91 | this.simpleShort = simpleShort; 92 | } 93 | 94 | public int getSimpleInt() { 95 | return simpleInt; 96 | } 97 | 98 | public void setSimpleInt(int simpleInt) { 99 | this.simpleInt = simpleInt; 100 | } 101 | 102 | public long getSimpleLong() { 103 | return simpleLong; 104 | } 105 | 106 | public void setSimpleLong(long simpleLong) { 107 | this.simpleLong = simpleLong; 108 | } 109 | 110 | public float getSimpleFloat() { 111 | return simpleFloat; 112 | } 113 | 114 | public void setSimpleFloat(float simpleFloat) { 115 | this.simpleFloat = simpleFloat; 116 | } 117 | 118 | public double getSimpleDouble() { 119 | return simpleDouble; 120 | } 121 | 122 | public void setSimpleDouble(double simpleDouble) { 123 | this.simpleDouble = simpleDouble; 124 | } 125 | 126 | public String getSimpleString() { 127 | return simpleString; 128 | } 129 | 130 | public void setSimpleString(String simpleString) { 131 | this.simpleString = simpleString; 132 | } 133 | 134 | public byte[] getSimpleByteArray() { 135 | return simpleByteArray; 136 | } 137 | 138 | public void setSimpleByteArray(byte[] simpleByteArray) { 139 | this.simpleByteArray = simpleByteArray; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/realm/RealmPerfTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.realm; 18 | 19 | import android.content.Context; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import io.objectbox.performanceapp.PerfTest; 25 | import io.objectbox.performanceapp.PerfTestRunner; 26 | import io.objectbox.performanceapp.TestType; 27 | import io.realm.Realm; 28 | import io.realm.RealmConfiguration; 29 | import io.realm.RealmResults; 30 | 31 | public class RealmPerfTest extends PerfTest { 32 | 33 | private boolean versionLoggedOnce; 34 | private Realm realm; 35 | 36 | @Override 37 | public String name() { 38 | return "Realm"; 39 | } 40 | 41 | public void setUp(Context context, PerfTestRunner testRunner) { 42 | super.setUp(context, testRunner); 43 | Realm.init(context); 44 | realm = Realm.getDefaultInstance(); 45 | 46 | RealmConfiguration configuration = realm.getConfiguration(); 47 | realm.close(); 48 | Realm.deleteRealm(configuration); 49 | realm = Realm.getDefaultInstance(); 50 | 51 | if (!versionLoggedOnce) { 52 | //log("Realm " + ??); 53 | versionLoggedOnce = true; 54 | } 55 | } 56 | 57 | @Override 58 | public void run(TestType type) { 59 | switch (type.name) { 60 | case TestType.CRUD: 61 | runBatchPerfTest(false); 62 | break; 63 | case TestType.CRUD_SCALARS: 64 | runBatchPerfTest(true); 65 | break; 66 | case TestType.CRUD_INDEXED: 67 | runBatchPerfTestIndexed(); 68 | break; 69 | case TestType.QUERY_STRING: 70 | runQueryByString(); 71 | break; 72 | case TestType.QUERY_STRING_INDEXED: 73 | runQueryByStringIndexed(); 74 | break; 75 | case TestType.QUERY_ID: 76 | runQueryById(false); 77 | break; 78 | case TestType.QUERY_ID_RANDOM: 79 | runQueryById(true); 80 | break; 81 | } 82 | } 83 | 84 | public void runBatchPerfTest(boolean scalarsOnly) { 85 | List list = new ArrayList<>(numberEntities); 86 | for (int i = 0; i < numberEntities; i++) { 87 | list.add(createEntity(i, scalarsOnly)); 88 | } 89 | startBenchmark("insert"); 90 | realm.beginTransaction(); 91 | realm.insert(list); 92 | realm.commitTransaction(); 93 | stopBenchmark(); 94 | 95 | for (SimpleEntity entity : list) { 96 | if (scalarsOnly) { 97 | setRandomScalars(entity); 98 | } else { 99 | setRandomValues(entity); 100 | } 101 | } 102 | startBenchmark("update"); 103 | realm.beginTransaction(); 104 | realm.insertOrUpdate(list); 105 | realm.commitTransaction(); 106 | stopBenchmark(); 107 | 108 | //noinspection UnusedAssignment 109 | list = null; 110 | 111 | startBenchmark("load"); 112 | RealmResults reloaded = realm.where(SimpleEntity.class).findAll(); 113 | stopBenchmark(); 114 | 115 | startBenchmark("access"); 116 | accessAll(reloaded); 117 | stopBenchmark(); 118 | 119 | startBenchmark("delete"); 120 | realm.beginTransaction(); 121 | reloaded.deleteAllFromRealm(); 122 | realm.commitTransaction(); 123 | stopBenchmark(); 124 | } 125 | 126 | protected void setRandomValues(SimpleEntity entity) { 127 | setRandomScalars(entity); 128 | entity.setSimpleString(randomString()); 129 | entity.setSimpleByteArray(randomBytes()); 130 | } 131 | 132 | private void setRandomScalars(SimpleEntity entity) { 133 | entity.setSimpleBoolean(random.nextBoolean()); 134 | entity.setSimpleByte((byte) random.nextInt()); 135 | entity.setSimpleShort((short) random.nextInt()); 136 | entity.setSimpleInt(random.nextInt()); 137 | entity.setSimpleLong(random.nextLong()); 138 | entity.setSimpleDouble(random.nextDouble()); 139 | entity.setSimpleFloat(random.nextFloat()); 140 | } 141 | 142 | public SimpleEntity createEntity(long id, boolean scalarsOnly) { 143 | SimpleEntity entity = new SimpleEntity(); 144 | entity.setId(id); 145 | if (scalarsOnly) { 146 | setRandomScalars(entity); 147 | } else { 148 | setRandomValues(entity); 149 | } 150 | return entity; 151 | } 152 | 153 | @SuppressWarnings("ResultOfMethodCallIgnored") 154 | protected void accessAll(List list) { 155 | for (SimpleEntity entity : list) { 156 | entity.getId(); 157 | entity.getSimpleBoolean(); 158 | entity.getSimpleByte(); 159 | entity.getSimpleShort(); 160 | entity.getSimpleInt(); 161 | entity.getSimpleLong(); 162 | entity.getSimpleFloat(); 163 | entity.getSimpleDouble(); 164 | entity.getSimpleString(); 165 | entity.getSimpleByteArray(); 166 | } 167 | } 168 | 169 | public void runBatchPerfTestIndexed() { 170 | List list = new ArrayList<>(numberEntities); 171 | for (int i = 0; i < numberEntities; i++) { 172 | list.add(createEntityIndexed(i)); 173 | } 174 | startBenchmark("insert"); 175 | realm.beginTransaction(); 176 | realm.insert(list); 177 | realm.commitTransaction(); 178 | stopBenchmark(); 179 | 180 | for (SimpleEntityIndexed entity : list) { 181 | setRandomValues(entity); 182 | } 183 | startBenchmark("update"); 184 | realm.beginTransaction(); 185 | realm.insertOrUpdate(list); 186 | realm.commitTransaction(); 187 | stopBenchmark(); 188 | 189 | //noinspection UnusedAssignment 190 | list = null; 191 | 192 | startBenchmark("load"); 193 | RealmResults reloaded = realm.where(SimpleEntityIndexed.class).findAll(); 194 | stopBenchmark(); 195 | 196 | startBenchmark("access"); 197 | accessAllIndexed(reloaded); 198 | stopBenchmark(); 199 | 200 | startBenchmark("delete"); 201 | realm.beginTransaction(); 202 | reloaded.deleteAllFromRealm(); 203 | realm.commitTransaction(); 204 | stopBenchmark(); 205 | } 206 | 207 | protected void setRandomValues(SimpleEntityIndexed entity) { 208 | entity.setSimpleBoolean(random.nextBoolean()); 209 | entity.setSimpleByte((byte) random.nextInt()); 210 | entity.setSimpleShort((short) random.nextInt()); 211 | entity.setSimpleInt(random.nextInt()); 212 | entity.setSimpleLong(random.nextLong()); 213 | entity.setSimpleDouble(random.nextDouble()); 214 | entity.setSimpleFloat(random.nextFloat()); 215 | entity.setSimpleString(randomString()); 216 | entity.setSimpleByteArray(randomBytes()); 217 | } 218 | 219 | public SimpleEntityIndexed createEntityIndexed(long id) { 220 | SimpleEntityIndexed entity = new SimpleEntityIndexed(); 221 | entity.setId(id); 222 | setRandomValues(entity); 223 | return entity; 224 | } 225 | 226 | @SuppressWarnings("ResultOfMethodCallIgnored") 227 | protected void accessAllIndexed(List list) { 228 | for (SimpleEntityIndexed entity : list) { 229 | entity.getId(); 230 | entity.getSimpleBoolean(); 231 | entity.getSimpleByte(); 232 | entity.getSimpleShort(); 233 | entity.getSimpleInt(); 234 | entity.getSimpleLong(); 235 | entity.getSimpleFloat(); 236 | entity.getSimpleDouble(); 237 | entity.getSimpleString(); 238 | entity.getSimpleByteArray(); 239 | } 240 | } 241 | 242 | private void runQueryByString() { 243 | if (numberEntities > 10000) { 244 | log("Reduce number of entities to 10000 to avoid extremely long test runs"); 245 | return; 246 | } 247 | List entities = new ArrayList<>(numberEntities); 248 | for (int i = 0; i < numberEntities; i++) { 249 | entities.add(createEntity(i, false)); 250 | } 251 | 252 | startBenchmark("insert"); 253 | realm.beginTransaction(); 254 | realm.insert(entities); 255 | realm.commitTransaction(); 256 | stopBenchmark(); 257 | 258 | final String[] stringsToLookup = new String[numberEntities]; 259 | for (int i = 0; i < numberEntities; i++) { 260 | String text = ""; 261 | while (text.length() < 2) { 262 | text = entities.get(random.nextInt(numberEntities)).getSimpleString(); 263 | } 264 | stringsToLookup[i] = text; 265 | } 266 | 267 | startBenchmark("query"); 268 | long entitiesFound = 0; 269 | for (int i = 0; i < numberEntities; i++) { 270 | List result = realm.where(SimpleEntity.class).equalTo("simpleString", stringsToLookup[i]).findAll(); 271 | accessAll(result); 272 | entitiesFound += result.size(); 273 | } 274 | stopBenchmark(); 275 | log("Entities found: " + entitiesFound); 276 | } 277 | 278 | private void runQueryByStringIndexed() { 279 | List entities = new ArrayList<>(numberEntities); 280 | for (int i = 0; i < numberEntities; i++) { 281 | entities.add(createEntityIndexed(i)); 282 | } 283 | 284 | startBenchmark("insert"); 285 | realm.beginTransaction(); 286 | realm.insert(entities); 287 | realm.commitTransaction(); 288 | stopBenchmark(); 289 | 290 | final String[] stringsToLookup = new String[numberEntities]; 291 | for (int i = 0; i < numberEntities; i++) { 292 | String text = ""; 293 | while (text.length() < 2) { 294 | text = entities.get(random.nextInt(numberEntities)).getSimpleString(); 295 | } 296 | stringsToLookup[i] = text; 297 | } 298 | 299 | startBenchmark("query"); 300 | long entitiesFound = 0; 301 | for (int i = 0; i < numberEntities; i++) { 302 | List result = realm.where(SimpleEntityIndexed.class).equalTo("simpleString", stringsToLookup[i]).findAll(); 303 | accessAllIndexed(result); 304 | entitiesFound += result.size(); 305 | } 306 | stopBenchmark(); 307 | log("Entities found: " + entitiesFound); 308 | } 309 | 310 | private void runQueryById(boolean randomIds) { 311 | List entities = new ArrayList<>(numberEntities); 312 | for (int i = 0; i < numberEntities; i++) { 313 | entities.add(createEntity((long) i, false)); 314 | } 315 | 316 | startBenchmark("insert"); 317 | realm.beginTransaction(); 318 | realm.insert(entities); 319 | realm.commitTransaction(); 320 | stopBenchmark(); 321 | 322 | long[] idsToLookup = new long[numberEntities]; 323 | for (int i = 0; i < numberEntities; i++) { 324 | idsToLookup[i] = randomIds ? random.nextInt(numberEntities) : i; 325 | } 326 | 327 | startBenchmark("query"); 328 | for (int i = 0; i < numberEntities; i++) { 329 | SimpleEntity entity = realm.where(SimpleEntity.class).equalTo("id", idsToLookup[i]).findFirst(); 330 | accessAll(entity); 331 | } 332 | stopBenchmark(); 333 | } 334 | 335 | @SuppressWarnings("ResultOfMethodCallIgnored") 336 | private void accessAll(SimpleEntity entity) { 337 | entity.getId(); 338 | entity.getSimpleBoolean(); 339 | entity.getSimpleByte(); 340 | entity.getSimpleShort(); 341 | entity.getSimpleInt(); 342 | entity.getSimpleLong(); 343 | entity.getSimpleFloat(); 344 | entity.getSimpleDouble(); 345 | entity.getSimpleString(); 346 | entity.getSimpleByteArray(); 347 | } 348 | 349 | @Override 350 | public void tearDown() { 351 | RealmConfiguration configuration = realm.getConfiguration(); 352 | realm.close(); 353 | Realm.deleteRealm(configuration); 354 | } 355 | 356 | } 357 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/realm/SimpleEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.realm; 18 | 19 | import io.realm.RealmObject; 20 | import io.realm.annotations.PrimaryKey; 21 | 22 | public class SimpleEntity extends RealmObject { 23 | 24 | @PrimaryKey 25 | private long id; 26 | private boolean simpleBoolean; 27 | private byte simpleByte; 28 | private short simpleShort; 29 | private int simpleInt; 30 | private long simpleLong; 31 | private float simpleFloat; 32 | private double simpleDouble; 33 | 34 | private String simpleString; 35 | 36 | private byte[] simpleByteArray; 37 | 38 | public SimpleEntity() { 39 | } 40 | 41 | public long getId() { 42 | return id; 43 | } 44 | 45 | public void setId(long id) { 46 | this.id = id; 47 | } 48 | 49 | public boolean getSimpleBoolean() { 50 | return simpleBoolean; 51 | } 52 | 53 | public void setSimpleBoolean(boolean simpleBoolean) { 54 | this.simpleBoolean = simpleBoolean; 55 | } 56 | 57 | public byte getSimpleByte() { 58 | return simpleByte; 59 | } 60 | 61 | public void setSimpleByte(byte simpleByte) { 62 | this.simpleByte = simpleByte; 63 | } 64 | 65 | public short getSimpleShort() { 66 | return simpleShort; 67 | } 68 | 69 | public void setSimpleShort(short simpleShort) { 70 | this.simpleShort = simpleShort; 71 | } 72 | 73 | public int getSimpleInt() { 74 | return simpleInt; 75 | } 76 | 77 | public void setSimpleInt(int simpleInt) { 78 | this.simpleInt = simpleInt; 79 | } 80 | 81 | public long getSimpleLong() { 82 | return simpleLong; 83 | } 84 | 85 | public void setSimpleLong(long simpleLong) { 86 | this.simpleLong = simpleLong; 87 | } 88 | 89 | public float getSimpleFloat() { 90 | return simpleFloat; 91 | } 92 | 93 | public void setSimpleFloat(float simpleFloat) { 94 | this.simpleFloat = simpleFloat; 95 | } 96 | 97 | public double getSimpleDouble() { 98 | return simpleDouble; 99 | } 100 | 101 | public void setSimpleDouble(double simpleDouble) { 102 | this.simpleDouble = simpleDouble; 103 | } 104 | 105 | public String getSimpleString() { 106 | return simpleString; 107 | } 108 | 109 | public void setSimpleString(String simpleString) { 110 | this.simpleString = simpleString; 111 | } 112 | 113 | 114 | public byte[] getSimpleByteArray() { 115 | return simpleByteArray; 116 | } 117 | 118 | public void setSimpleByteArray(byte[] simpleByteArray) { 119 | this.simpleByteArray = simpleByteArray; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/realm/SimpleEntityIndexed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 ObjectBox Ltd. All rights reserved. 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 io.objectbox.performanceapp.realm; 18 | 19 | import io.realm.RealmObject; 20 | import io.realm.annotations.Index; 21 | import io.realm.annotations.PrimaryKey; 22 | 23 | public class SimpleEntityIndexed extends RealmObject { 24 | 25 | @PrimaryKey 26 | private long id; 27 | private boolean simpleBoolean; 28 | private byte simpleByte; 29 | private short simpleShort; 30 | 31 | @Index 32 | private int simpleInt; 33 | private long simpleLong; 34 | private float simpleFloat; 35 | private double simpleDouble; 36 | 37 | @Index 38 | private String simpleString; 39 | 40 | private byte[] simpleByteArray; 41 | 42 | public SimpleEntityIndexed() { 43 | } 44 | 45 | public SimpleEntityIndexed(long id) { 46 | this.id = id; 47 | } 48 | 49 | public SimpleEntityIndexed(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) { 50 | this.id = id; 51 | this.simpleBoolean = simpleBoolean; 52 | this.simpleByte = simpleByte; 53 | this.simpleShort = simpleShort; 54 | this.simpleInt = simpleInt; 55 | this.simpleLong = simpleLong; 56 | this.simpleFloat = simpleFloat; 57 | this.simpleDouble = simpleDouble; 58 | this.simpleString = simpleString; 59 | this.simpleByteArray = simpleByteArray; 60 | } 61 | 62 | public long getId() { 63 | return id; 64 | } 65 | 66 | public void setId(long id) { 67 | this.id = id; 68 | } 69 | 70 | public boolean getSimpleBoolean() { 71 | return simpleBoolean; 72 | } 73 | 74 | public void setSimpleBoolean(boolean simpleBoolean) { 75 | this.simpleBoolean = simpleBoolean; 76 | } 77 | 78 | public byte getSimpleByte() { 79 | return simpleByte; 80 | } 81 | 82 | public void setSimpleByte(byte simpleByte) { 83 | this.simpleByte = simpleByte; 84 | } 85 | 86 | public short getSimpleShort() { 87 | return simpleShort; 88 | } 89 | 90 | public void setSimpleShort(short simpleShort) { 91 | this.simpleShort = simpleShort; 92 | } 93 | 94 | public int getSimpleInt() { 95 | return simpleInt; 96 | } 97 | 98 | public void setSimpleInt(int simpleInt) { 99 | this.simpleInt = simpleInt; 100 | } 101 | 102 | public long getSimpleLong() { 103 | return simpleLong; 104 | } 105 | 106 | public void setSimpleLong(long simpleLong) { 107 | this.simpleLong = simpleLong; 108 | } 109 | 110 | public float getSimpleFloat() { 111 | return simpleFloat; 112 | } 113 | 114 | public void setSimpleFloat(float simpleFloat) { 115 | this.simpleFloat = simpleFloat; 116 | } 117 | 118 | public double getSimpleDouble() { 119 | return simpleDouble; 120 | } 121 | 122 | public void setSimpleDouble(double simpleDouble) { 123 | this.simpleDouble = simpleDouble; 124 | } 125 | 126 | public String getSimpleString() { 127 | return simpleString; 128 | } 129 | 130 | public void setSimpleString(String simpleString) { 131 | this.simpleString = simpleString; 132 | } 133 | 134 | public byte[] getSimpleByteArray() { 135 | return simpleByteArray; 136 | } 137 | 138 | public void setSimpleByteArray(byte[] simpleByteArray) { 139 | this.simpleByteArray = simpleByteArray; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/room/AppDatabase.java: -------------------------------------------------------------------------------- 1 | package io.objectbox.performanceapp.room; 2 | 3 | import androidx.room.Database; 4 | import androidx.room.RoomDatabase; 5 | 6 | @Database(entities = {SimpleEntity.class, SimpleEntityIndexed.class}, version = 1) 7 | public abstract class AppDatabase extends RoomDatabase { 8 | 9 | public abstract SimpleEntityDao simpleEntityDao(); 10 | 11 | public abstract SimpleEntityIndexedDao simpleEntityIndexedDao(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/room/RoomPerfTest.java: -------------------------------------------------------------------------------- 1 | package io.objectbox.performanceapp.room; 2 | 3 | import androidx.room.Room; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import io.objectbox.performanceapp.PerfTest; 11 | import io.objectbox.performanceapp.PerfTestRunner; 12 | import io.objectbox.performanceapp.TestType; 13 | 14 | public class RoomPerfTest extends PerfTest { 15 | 16 | public static final String DB_NAME = "sqlite-room"; 17 | 18 | private boolean versionLoggedOnce; 19 | private AppDatabase db; 20 | private SimpleEntityDao dao; 21 | private SimpleEntityIndexedDao daoIndexed; 22 | 23 | @Override 24 | public String name() { 25 | return "Room"; 26 | } 27 | 28 | @Override 29 | public void setUp(Context context, PerfTestRunner testRunner) { 30 | super.setUp(context, testRunner); 31 | boolean deleted = context.deleteDatabase(DB_NAME); 32 | if (deleted) { 33 | log("DB existed before start - deleted"); 34 | } 35 | db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DB_NAME) 36 | .build(); 37 | dao = db.simpleEntityDao(); 38 | daoIndexed = db.simpleEntityIndexedDao(); 39 | 40 | if (!versionLoggedOnce) { 41 | try (Cursor cursor = db.query("select sqlite_version() AS sqlite_version", null)) { 42 | if (cursor.moveToFirst()) { 43 | log("SQLite version " + cursor.getString(0)); 44 | } 45 | } 46 | versionLoggedOnce = true; 47 | } 48 | } 49 | 50 | @Override 51 | public void run(TestType type) { 52 | switch (type.name) { 53 | case TestType.CRUD: 54 | runBatchPerfTest(false); 55 | break; 56 | case TestType.CRUD_SCALARS: 57 | runBatchPerfTest(true); 58 | break; 59 | case TestType.CRUD_INDEXED: 60 | runBatchPerfTestIndexed(); 61 | break; 62 | case TestType.QUERY_STRING: 63 | runQueryByString(); 64 | break; 65 | case TestType.QUERY_STRING_INDEXED: 66 | runQueryByStringIndexed(); 67 | break; 68 | case TestType.QUERY_INTEGER: 69 | runQueryByInteger(); 70 | break; 71 | case TestType.QUERY_INTEGER_INDEXED: 72 | runQueryByIntegerIndexed(); 73 | break; 74 | case TestType.QUERY_ID: 75 | runQueryById(false); 76 | break; 77 | case TestType.QUERY_ID_RANDOM: 78 | runQueryById(true); 79 | break; 80 | } 81 | } 82 | 83 | private void runBatchPerfTest(boolean scalarsOnly) { 84 | List list = new ArrayList<>(numberEntities); 85 | for (int i = 0; i < numberEntities; i++) { 86 | list.add(createEntity((long) i, scalarsOnly)); 87 | } 88 | startBenchmark("insert"); 89 | dao.insertInTx(list); 90 | stopBenchmark(); 91 | 92 | for (SimpleEntity entity : list) { 93 | if (scalarsOnly) { 94 | setRandomScalars(entity); 95 | } else { 96 | setRandomValues(entity); 97 | } 98 | } 99 | startBenchmark("update"); 100 | dao.updateInTx(list); 101 | stopBenchmark(); 102 | 103 | //noinspection UnusedAssignment 104 | list = null; 105 | 106 | startBenchmark("load"); 107 | List reloaded = dao.loadAll(); 108 | stopBenchmark(); 109 | 110 | startBenchmark("access"); 111 | accessAll(reloaded); 112 | stopBenchmark(); 113 | 114 | startBenchmark("delete"); 115 | dao.deleteInTx(reloaded); 116 | stopBenchmark(); 117 | } 118 | 119 | private void runBatchPerfTestIndexed() { 120 | List list = new ArrayList<>(numberEntities); 121 | for (int i = 0; i < numberEntities; i++) { 122 | list.add(createEntityIndexed((long) i)); 123 | } 124 | startBenchmark("insert"); 125 | daoIndexed.insertInTx(list); 126 | stopBenchmark(); 127 | 128 | for (SimpleEntityIndexed entity : list) { 129 | setRandomValues(entity); 130 | } 131 | startBenchmark("update"); 132 | daoIndexed.updateInTx(list); 133 | stopBenchmark(); 134 | 135 | //noinspection UnusedAssignment 136 | list = null; 137 | 138 | startBenchmark("load"); 139 | List reloaded = daoIndexed.loadAll(); 140 | stopBenchmark(); 141 | 142 | startBenchmark("access"); 143 | accessAllIndexed(reloaded); 144 | stopBenchmark(); 145 | 146 | startBenchmark("delete"); 147 | daoIndexed.deleteInTx(reloaded); 148 | stopBenchmark(); 149 | } 150 | 151 | private void runQueryByString() { 152 | if (numberEntities > 10000) { 153 | log("Reduce number of entities to 10000 to avoid extremely long test runs"); 154 | return; 155 | } 156 | List entities = new ArrayList<>(numberEntities); 157 | for (int i = 0; i < numberEntities; i++) { 158 | entities.add(createEntity((long) i, false)); 159 | } 160 | 161 | startBenchmark("insert"); 162 | dao.insertInTx(entities); 163 | stopBenchmark(); 164 | 165 | String[] stringsToLookup = new String[numberEntities]; 166 | for (int i = 0; i < numberEntities; i++) { 167 | String text = ""; 168 | while (text.length() < 2) { 169 | text = entities.get(random.nextInt(numberEntities)).getSimpleString(); 170 | } 171 | stringsToLookup[i] = text; 172 | } 173 | 174 | startBenchmark("query"); 175 | long entitiesFound = db.runInTransaction(() -> { 176 | long found = 0; 177 | for (int i = 0; i < numberEntities; i++) { 178 | List result = dao.whereSimpleStringEq(stringsToLookup[i]); 179 | accessAll(result); 180 | found += result.size(); 181 | } 182 | return found; 183 | }); 184 | stopBenchmark(); 185 | log("Entities found: " + entitiesFound); 186 | } 187 | 188 | private void runQueryByStringIndexed() { 189 | List entities = new ArrayList<>(numberEntities); 190 | for (int i = 0; i < numberEntities; i++) { 191 | entities.add(createEntityIndexed((long) i)); 192 | } 193 | 194 | startBenchmark("insert"); 195 | daoIndexed.insertInTx(entities); 196 | stopBenchmark(); 197 | 198 | String[] stringsToLookup = new String[numberEntities]; 199 | for (int i = 0; i < numberEntities; i++) { 200 | String text = ""; 201 | while (text.length() < 2) { 202 | text = entities.get(random.nextInt(numberEntities)).getSimpleString(); 203 | } 204 | stringsToLookup[i] = text; 205 | } 206 | 207 | startBenchmark("query"); 208 | long entitiesFound = db.runInTransaction(() -> { 209 | long found = 0; 210 | for (int i = 0; i < numberEntities; i++) { 211 | List result = daoIndexed.whereSimpleStringEq(stringsToLookup[i]); 212 | accessAllIndexed(result); 213 | found += result.size(); 214 | } 215 | return found; 216 | }); 217 | stopBenchmark(); 218 | log("Entities found: " + entitiesFound); 219 | } 220 | 221 | private void runQueryByInteger() { 222 | if (numberEntities > 10000) { 223 | log("Reduce number of entities to 10000 to avoid extremely long test runs"); 224 | return; 225 | } 226 | List entities = new ArrayList<>(numberEntities); 227 | for (int i = 0; i < numberEntities; i++) { 228 | entities.add(createEntity((long) i, false)); 229 | } 230 | 231 | startBenchmark("insert"); 232 | dao.insertInTx(entities); 233 | stopBenchmark(); 234 | 235 | final int[] valuesToLookup = new int[numberEntities]; 236 | for (int i = 0; i < numberEntities; i++) { 237 | valuesToLookup[i] = entities.get(random.nextInt(numberEntities)).getSimpleInt(); 238 | } 239 | 240 | startBenchmark("query"); 241 | long entitiesFound = 0; 242 | for (int i = 0; i < numberEntities; i++) { 243 | List result = dao.whereSimpleIntEq(valuesToLookup[i]); 244 | accessAll(result); 245 | entitiesFound += result.size(); 246 | } 247 | stopBenchmark(); 248 | log("Entities found: " + entitiesFound); 249 | assertGreaterOrEqualToNumberOfEntities(entitiesFound); 250 | } 251 | 252 | private void runQueryByIntegerIndexed() { 253 | List entities = new ArrayList<>(numberEntities); 254 | for (int i = 0; i < numberEntities; i++) { 255 | entities.add(createEntityIndexed((long) i)); 256 | } 257 | 258 | startBenchmark("insert"); 259 | daoIndexed.insertInTx(entities); 260 | stopBenchmark(); 261 | 262 | final int[] valuesToLookup = new int[numberEntities]; 263 | for (int i = 0; i < numberEntities; i++) { 264 | valuesToLookup[i] = entities.get(random.nextInt(numberEntities)).getSimpleInt(); 265 | } 266 | 267 | startBenchmark("query"); 268 | long entitiesFound = 0; 269 | for (int i = 0; i < numberEntities; i++) { 270 | List result = daoIndexed.whereSimpleIntEq(valuesToLookup[i]); 271 | accessAllIndexed(result); 272 | entitiesFound += result.size(); 273 | } 274 | stopBenchmark(); 275 | log("Entities found: " + entitiesFound); 276 | assertGreaterOrEqualToNumberOfEntities(entitiesFound); 277 | } 278 | 279 | private void runQueryById(boolean randomIds) { 280 | List entities = new ArrayList<>(numberEntities); 281 | for (int i = 0; i < numberEntities; i++) { 282 | entities.add(createEntity((long) i, false)); 283 | } 284 | 285 | startBenchmark("insert"); 286 | dao.insertInTx(entities); 287 | stopBenchmark(); 288 | 289 | assertEntityCount(dao.count()); 290 | 291 | long[] idsToLookup = new long[numberEntities]; 292 | for (int i = 0; i < numberEntities; i++) { 293 | idsToLookup[i] = randomIds ? random.nextInt(numberEntities) : i; 294 | } 295 | 296 | startBenchmark("query"); 297 | for (int i = 0; i < numberEntities; i++) { 298 | SimpleEntity entity = dao.load(idsToLookup[i]); 299 | accessAll(entity); 300 | } 301 | stopBenchmark(); 302 | } 303 | 304 | @Override 305 | public void tearDown() { 306 | super.tearDown(); 307 | db.close(); 308 | boolean deleted = context.deleteDatabase(DB_NAME); 309 | log("DB deleted: " + deleted); 310 | } 311 | 312 | @SuppressWarnings("ResultOfMethodCallIgnored") 313 | private void accessAll(SimpleEntity entity) { 314 | entity.getId(); 315 | entity.getSimpleBoolean(); 316 | entity.getSimpleByte(); 317 | entity.getSimpleShort(); 318 | entity.getSimpleInt(); 319 | entity.getSimpleLong(); 320 | entity.getSimpleFloat(); 321 | entity.getSimpleDouble(); 322 | entity.getSimpleString(); 323 | entity.getSimpleByteArray(); 324 | } 325 | 326 | @SuppressWarnings("ResultOfMethodCallIgnored") 327 | protected void accessAll(List list) { 328 | for (SimpleEntity entity : list) { 329 | entity.getId(); 330 | entity.getSimpleBoolean(); 331 | entity.getSimpleByte(); 332 | entity.getSimpleShort(); 333 | entity.getSimpleInt(); 334 | entity.getSimpleLong(); 335 | entity.getSimpleFloat(); 336 | entity.getSimpleDouble(); 337 | entity.getSimpleString(); 338 | entity.getSimpleByteArray(); 339 | } 340 | } 341 | 342 | @SuppressWarnings("ResultOfMethodCallIgnored") 343 | protected void accessAllIndexed(List list) { 344 | for (SimpleEntityIndexed entity : list) { 345 | entity.getId(); 346 | entity.getSimpleBoolean(); 347 | entity.getSimpleByte(); 348 | entity.getSimpleShort(); 349 | entity.getSimpleInt(); 350 | entity.getSimpleLong(); 351 | entity.getSimpleFloat(); 352 | entity.getSimpleDouble(); 353 | entity.getSimpleString(); 354 | entity.getSimpleByteArray(); 355 | } 356 | } 357 | 358 | private void setRandomValues(SimpleEntity entity) { 359 | setRandomScalars(entity); 360 | entity.setSimpleString(randomString()); 361 | entity.setSimpleByteArray(randomBytes()); 362 | } 363 | 364 | protected void setRandomValues(SimpleEntityIndexed entity) { 365 | entity.setSimpleBoolean(random.nextBoolean()); 366 | entity.setSimpleByte((byte) random.nextInt()); 367 | entity.setSimpleShort((short) random.nextInt()); 368 | entity.setSimpleInt(random.nextInt()); 369 | entity.setSimpleLong(random.nextLong()); 370 | entity.setSimpleDouble(random.nextDouble()); 371 | entity.setSimpleFloat(random.nextFloat()); 372 | entity.setSimpleString(randomString()); 373 | entity.setSimpleByteArray(randomBytes()); 374 | } 375 | 376 | private void setRandomScalars(SimpleEntity entity) { 377 | entity.setSimpleBoolean(random.nextBoolean()); 378 | entity.setSimpleByte((byte) random.nextInt()); 379 | entity.setSimpleShort((short) random.nextInt()); 380 | entity.setSimpleInt(random.nextInt()); 381 | entity.setSimpleLong(random.nextLong()); 382 | entity.setSimpleDouble(random.nextDouble()); 383 | entity.setSimpleFloat(random.nextFloat()); 384 | } 385 | 386 | private SimpleEntity createEntity(Long key, boolean scalarsOnly) { 387 | SimpleEntity entity = new SimpleEntity(); 388 | if (key != null) { 389 | entity.setId(key); 390 | } 391 | if (scalarsOnly) { 392 | setRandomScalars(entity); 393 | } else { 394 | setRandomValues(entity); 395 | } 396 | return entity; 397 | } 398 | 399 | public SimpleEntityIndexed createEntityIndexed(Long key) { 400 | SimpleEntityIndexed entity = new SimpleEntityIndexed(); 401 | if (key != null) { 402 | entity.setId(key); 403 | } 404 | setRandomValues(entity); 405 | return entity; 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/room/SimpleEntity.java: -------------------------------------------------------------------------------- 1 | package io.objectbox.performanceapp.room; 2 | 3 | import androidx.room.Entity; 4 | import androidx.room.PrimaryKey; 5 | 6 | @Entity 7 | public class SimpleEntity { 8 | 9 | @PrimaryKey 10 | long id; 11 | boolean simpleBoolean; 12 | byte simpleByte; 13 | short simpleShort; 14 | int simpleInt; 15 | long simpleLong; 16 | float simpleFloat; 17 | double simpleDouble; 18 | 19 | String simpleString; 20 | 21 | byte[] simpleByteArray; 22 | 23 | public long getId() { 24 | return id; 25 | } 26 | 27 | public void setId(long id) { 28 | this.id = id; 29 | } 30 | 31 | public boolean getSimpleBoolean() { 32 | return simpleBoolean; 33 | } 34 | 35 | public void setSimpleBoolean(boolean simpleBoolean) { 36 | this.simpleBoolean = simpleBoolean; 37 | } 38 | 39 | public byte getSimpleByte() { 40 | return simpleByte; 41 | } 42 | 43 | public void setSimpleByte(byte simpleByte) { 44 | this.simpleByte = simpleByte; 45 | } 46 | 47 | public short getSimpleShort() { 48 | return simpleShort; 49 | } 50 | 51 | public void setSimpleShort(short simpleShort) { 52 | this.simpleShort = simpleShort; 53 | } 54 | 55 | public int getSimpleInt() { 56 | return simpleInt; 57 | } 58 | 59 | public void setSimpleInt(int simpleInt) { 60 | this.simpleInt = simpleInt; 61 | } 62 | 63 | public long getSimpleLong() { 64 | return simpleLong; 65 | } 66 | 67 | public void setSimpleLong(long simpleLong) { 68 | this.simpleLong = simpleLong; 69 | } 70 | 71 | public float getSimpleFloat() { 72 | return simpleFloat; 73 | } 74 | 75 | public void setSimpleFloat(float simpleFloat) { 76 | this.simpleFloat = simpleFloat; 77 | } 78 | 79 | public double getSimpleDouble() { 80 | return simpleDouble; 81 | } 82 | 83 | public void setSimpleDouble(double simpleDouble) { 84 | this.simpleDouble = simpleDouble; 85 | } 86 | 87 | public String getSimpleString() { 88 | return simpleString; 89 | } 90 | 91 | public void setSimpleString(String simpleString) { 92 | this.simpleString = simpleString; 93 | } 94 | 95 | public byte[] getSimpleByteArray() { 96 | return simpleByteArray; 97 | } 98 | 99 | public void setSimpleByteArray(byte[] simpleByteArray) { 100 | this.simpleByteArray = simpleByteArray; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/room/SimpleEntityDao.java: -------------------------------------------------------------------------------- 1 | package io.objectbox.performanceapp.room; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.Query; 7 | import androidx.room.Update; 8 | 9 | import java.util.List; 10 | 11 | @Dao 12 | public interface SimpleEntityDao { 13 | 14 | @Insert 15 | void insertInTx(List entities); 16 | 17 | @Query("SELECT * from simpleentity where id = :id LIMIT 1") 18 | SimpleEntity load(long id); 19 | 20 | @Query("SELECT * FROM simpleentity") 21 | List loadAll(); 22 | 23 | @Update 24 | void updateInTx(List entities); 25 | 26 | @Delete 27 | void deleteInTx(List entities); 28 | 29 | @Query("SELECT * from simpleentity where simpleInt = :value") 30 | List whereSimpleIntEq(int value); 31 | 32 | @Query("SELECT * FROM simpleentity WHERE simpleString = :value") 33 | List whereSimpleStringEq(String value); 34 | 35 | @Query("SELECT COUNT(*) from simpleentity") 36 | int count(); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/room/SimpleEntityIndexed.java: -------------------------------------------------------------------------------- 1 | package io.objectbox.performanceapp.room; 2 | 3 | import androidx.room.Entity; 4 | import androidx.room.Index; 5 | import androidx.room.PrimaryKey; 6 | 7 | @Entity(indices = {@Index("simpleInt"), @Index("simpleString")}) 8 | public class SimpleEntityIndexed { 9 | 10 | @PrimaryKey 11 | private long id; 12 | private boolean simpleBoolean; 13 | private byte simpleByte; 14 | private short simpleShort; 15 | // index 16 | private int simpleInt; 17 | private long simpleLong; 18 | private float simpleFloat; 19 | private double simpleDouble; 20 | 21 | // index 22 | private String simpleString; 23 | 24 | private byte[] simpleByteArray; 25 | 26 | public long getId() { 27 | return id; 28 | } 29 | 30 | public void setId(long id) { 31 | this.id = id; 32 | } 33 | 34 | public boolean getSimpleBoolean() { 35 | return simpleBoolean; 36 | } 37 | 38 | public void setSimpleBoolean(boolean simpleBoolean) { 39 | this.simpleBoolean = simpleBoolean; 40 | } 41 | 42 | public byte getSimpleByte() { 43 | return simpleByte; 44 | } 45 | 46 | public void setSimpleByte(byte simpleByte) { 47 | this.simpleByte = simpleByte; 48 | } 49 | 50 | public short getSimpleShort() { 51 | return simpleShort; 52 | } 53 | 54 | public void setSimpleShort(short simpleShort) { 55 | this.simpleShort = simpleShort; 56 | } 57 | 58 | public int getSimpleInt() { 59 | return simpleInt; 60 | } 61 | 62 | public void setSimpleInt(int simpleInt) { 63 | this.simpleInt = simpleInt; 64 | } 65 | 66 | public long getSimpleLong() { 67 | return simpleLong; 68 | } 69 | 70 | public void setSimpleLong(long simpleLong) { 71 | this.simpleLong = simpleLong; 72 | } 73 | 74 | public float getSimpleFloat() { 75 | return simpleFloat; 76 | } 77 | 78 | public void setSimpleFloat(float simpleFloat) { 79 | this.simpleFloat = simpleFloat; 80 | } 81 | 82 | public double getSimpleDouble() { 83 | return simpleDouble; 84 | } 85 | 86 | public void setSimpleDouble(double simpleDouble) { 87 | this.simpleDouble = simpleDouble; 88 | } 89 | 90 | public String getSimpleString() { 91 | return simpleString; 92 | } 93 | 94 | public void setSimpleString(String simpleString) { 95 | this.simpleString = simpleString; 96 | } 97 | 98 | public byte[] getSimpleByteArray() { 99 | return simpleByteArray; 100 | } 101 | 102 | public void setSimpleByteArray(byte[] simpleByteArray) { 103 | this.simpleByteArray = simpleByteArray; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/io/objectbox/performanceapp/room/SimpleEntityIndexedDao.java: -------------------------------------------------------------------------------- 1 | package io.objectbox.performanceapp.room; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.Query; 7 | import androidx.room.Update; 8 | 9 | import java.util.List; 10 | 11 | @Dao 12 | public interface SimpleEntityIndexedDao { 13 | 14 | @Insert 15 | void insertInTx(List entities); 16 | 17 | @Query("SELECT * FROM simpleentityindexed") 18 | List loadAll(); 19 | 20 | @Update 21 | void updateInTx(List entities); 22 | 23 | @Delete 24 | void deleteInTx(List entities); 25 | 26 | @Query("SELECT * from simpleentityindexed where simpleInt = :value") 27 | List whereSimpleIntEq(int value); 28 | 29 | @Query("SELECT * FROM simpleentityindexed WHERE simpleString = :value") 30 | List whereSimpleStringEq(String value); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 25 | 26 | 36 | 37 | 45 | 46 | 56 | 57 | 66 | 67 | 75 | 76 | 77 | 86 | 87 | 88 | 98 | 99 | 100 | 112 | 113 |