2016-02-15 21:52:38 +00:00
|
|
|
/**
|
|
|
|
* Sample React Native App
|
|
|
|
* https://github.com/facebook/react-native
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
import React, {
|
|
|
|
AppRegistry,
|
|
|
|
Component,
|
|
|
|
StyleSheet,
|
|
|
|
Text,
|
|
|
|
View,
|
|
|
|
ListView,
|
|
|
|
TouchableHighlight
|
|
|
|
} from 'react-native';
|
|
|
|
|
|
|
|
const Store = require('react-native-store');
|
|
|
|
const SQLite = require('react-native-sqlite-storage');
|
|
|
|
const Realm = require('realm');
|
|
|
|
|
|
|
|
// Make SQLite module use Promises.
|
|
|
|
SQLite.enablePromise(true);
|
|
|
|
|
|
|
|
const TestObjectSchema = {
|
|
|
|
name: "TestObject",
|
|
|
|
properties: {
|
|
|
|
int: "int",
|
|
|
|
double: "double",
|
|
|
|
date: "date",
|
|
|
|
string: "string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const TestObjectListSchema = {
|
|
|
|
name: "TestObjectList",
|
|
|
|
properties: {
|
|
|
|
list: {type: "list", objectType: "TestObject", default: undefined}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-15 22:12:26 +00:00
|
|
|
const numTestObjects = 2000;
|
2016-02-15 21:52:38 +00:00
|
|
|
const numBatchTestObjects = numTestObjects * 100;
|
|
|
|
const numRepeats = 1;
|
|
|
|
const numQueryBuckets = 5;
|
|
|
|
|
|
|
|
class Tests {
|
|
|
|
async setup(testName) {
|
|
|
|
if (testName == 'enumeration') {
|
|
|
|
await this.batchInsert(this.testObjects(numTestObjects));
|
|
|
|
}
|
|
|
|
if (testName == 'querycount' || testName == 'queryenum') {
|
|
|
|
await this.batchInsert(this.testObjects(numBatchTestObjects));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async binsertions() {
|
|
|
|
await this.batchInsert(this.testObjects(numBatchTestObjects));
|
|
|
|
}
|
|
|
|
|
|
|
|
objectValues(object) {
|
|
|
|
return Object.keys(TestObjectSchema.properties).map((prop) => object[prop])
|
|
|
|
}
|
|
|
|
|
|
|
|
testObjects(count) {
|
|
|
|
let objects = [];
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
|
|
objects.push({ int: i % numQueryBuckets, double: i, date: new Date(i), string: "" + i });
|
|
|
|
}
|
|
|
|
return objects;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RealmTests extends Tests {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.name = 'Realm';
|
|
|
|
}
|
|
|
|
|
|
|
|
async setup(testName) {
|
|
|
|
Realm.clearTestState();
|
|
|
|
this.realm = new Realm({schema: [TestObjectSchema, TestObjectListSchema]});
|
|
|
|
|
|
|
|
await super.setup(testName);
|
|
|
|
}
|
|
|
|
|
|
|
|
async insertions() {
|
|
|
|
var realm = this.realm;
|
2016-02-15 22:12:26 +00:00
|
|
|
for (let i = 0; i < numTestObjects; i++) {
|
|
|
|
realm.write(() => {
|
2016-02-15 21:52:38 +00:00
|
|
|
realm.create("TestObject", { int: i % numQueryBuckets, double: i, date: new Date(i), string: "" + i });
|
2016-02-15 22:12:26 +00:00
|
|
|
});
|
|
|
|
}
|
2016-02-15 21:52:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async batchInsert(objects) {
|
|
|
|
var realm = this.realm;
|
|
|
|
realm.write(() => {
|
|
|
|
realm.create("TestObjectList", { list: objects});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async enumeration() {
|
|
|
|
let objects = this.realm.objects('TestObject');
|
|
|
|
let len = objects.length;
|
|
|
|
var obj;
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
obj = objects[i];
|
|
|
|
obj.int;
|
|
|
|
obj.double;
|
|
|
|
obj.date;
|
|
|
|
obj.string;
|
|
|
|
}
|
|
|
|
console.log("Enumerated " + len + " last: ", this.objectValues(obj));
|
|
|
|
}
|
|
|
|
|
|
|
|
async querycount() {
|
|
|
|
let objects = this.realm.objects('TestObject', 'int = 0 and double < ' + numTestObjects / 2);
|
|
|
|
let len = objects.length;
|
|
|
|
console.log("length: " + len + " last: ", this.objectValues(objects[len - 1]));
|
|
|
|
}
|
|
|
|
|
|
|
|
async queryenum() {
|
|
|
|
let objects = this.realm.objects('TestObject', 'int = 0 and double < ' + numTestObjects / 2);
|
|
|
|
var obj;
|
|
|
|
let len = objects.length;
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
obj = objects[i];
|
|
|
|
obj.int;
|
|
|
|
obj.double;
|
|
|
|
obj.date;
|
|
|
|
obj.string;
|
|
|
|
}
|
|
|
|
console.log("Enumerated " + len + " last: ", this.objectValues(obj));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RNStoreTests extends Tests {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.name = "RNStore";
|
|
|
|
}
|
|
|
|
|
|
|
|
async setup(testName) {
|
|
|
|
this.db = Store.model('objects');
|
|
|
|
await this.db.destroy();
|
|
|
|
|
|
|
|
await super.setup(testName);
|
|
|
|
}
|
|
|
|
|
|
|
|
async insertions() {
|
|
|
|
for (var obj of this.testObjects(numTestObjects)) {
|
|
|
|
await this.db.add(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async batchInsert(objects) {
|
|
|
|
await this.db.multiAdd(objects);
|
|
|
|
}
|
|
|
|
|
|
|
|
async enumeration() {
|
|
|
|
let objects = await this.db.find();
|
|
|
|
let len = objects.length;
|
|
|
|
var obj;
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
obj = objects[i];
|
|
|
|
obj.int;
|
|
|
|
obj.double;
|
|
|
|
obj.date;
|
|
|
|
obj.string;
|
|
|
|
}
|
|
|
|
console.log("Enumerated " + len + " last: ", this.objectValues(obj));
|
|
|
|
}
|
|
|
|
|
|
|
|
async querycount() {
|
|
|
|
let objects = await this.db.find({
|
|
|
|
where: {
|
|
|
|
and: [{ int: 0 }, { double: { lt: numTestObjects / 2 } }]
|
|
|
|
},
|
|
|
|
order: {
|
|
|
|
age: 'ASC',
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let len = objects.length;
|
|
|
|
console.log("length: " + len + " last: ", this.objectValues(objects[len - 1]));
|
|
|
|
}
|
|
|
|
|
|
|
|
async queryenum() {
|
|
|
|
let objects = await this.db.find({
|
|
|
|
where: {
|
|
|
|
and: [{ int: 0 }, { double: { lt: numTestObjects / 2 } }]
|
|
|
|
},
|
|
|
|
order: {
|
|
|
|
age: 'ASC',
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let len = objects.length;
|
|
|
|
var obj;
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
obj = objects[i];
|
|
|
|
obj.int;
|
|
|
|
obj.double;
|
|
|
|
obj.date;
|
|
|
|
obj.string;
|
|
|
|
}
|
|
|
|
console.log("Enumerated " + len + " last: ", this.objectValues(obj));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RNSqliteTests extends Tests {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.name = "RNSqlite";
|
|
|
|
}
|
|
|
|
|
|
|
|
async setup(testName) {
|
|
|
|
try {
|
|
|
|
await SQLite.deleteDatabase('test.db');
|
|
|
|
} catch (e) {}
|
|
|
|
|
|
|
|
this.db = await SQLite.openDatabase("test.db", "1.0", "Test Database", 200000);
|
|
|
|
|
|
|
|
await this.db.transaction((tx) => {
|
|
|
|
tx.executeSql('CREATE TABLE t1 (string VARCHAR(100), int INTEGER, double REAL, date INTEGER);');
|
|
|
|
});
|
|
|
|
|
|
|
|
await super.setup(testName);
|
|
|
|
}
|
|
|
|
|
|
|
|
async insertions() {
|
2016-02-15 22:12:26 +00:00
|
|
|
for (let i = 0; i < numTestObjects; i++) {
|
|
|
|
let values = ["" + i, i % numQueryBuckets, i, new Date(i).getTime()];
|
|
|
|
await this.db.transaction((tx) => {
|
2016-02-15 21:52:38 +00:00
|
|
|
tx.executeSql('INSERT INTO t1 (string, int, double, date) VALUES (?,?,?,?);', values);
|
2016-02-15 22:12:26 +00:00
|
|
|
});
|
|
|
|
}
|
2016-02-15 21:52:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async batchInsert(objects) {
|
|
|
|
await this.db.transaction((tx) => {
|
2016-02-15 22:12:26 +00:00
|
|
|
for (let i = 0; i < numTestObjects; i++) {
|
|
|
|
let values = ["" + i, i % numQueryBuckets, i, new Date(i).getTime()];
|
2016-02-15 21:52:38 +00:00
|
|
|
tx.executeSql('INSERT INTO t1 (string, int, double, date) VALUES (?,?,?,?);', values);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async enumeration() {
|
|
|
|
await this.db.readTransaction(async (tx) => {
|
|
|
|
let [, results] = await tx.executeSql('SELECT * FROM t1;')
|
|
|
|
let len = results.rows.length;
|
|
|
|
var row;
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
row = results.rows.item(i);
|
|
|
|
row.int;
|
|
|
|
row.double;
|
|
|
|
row.string;
|
|
|
|
new Date(row.date);
|
|
|
|
}
|
|
|
|
console.log("length: " + len + " last: ", this.objectValues(row));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async querycount() {
|
|
|
|
await this.db.readTransaction(async (tx) => {
|
|
|
|
let [, results] = await tx.executeSql('SELECT * FROM t1 WHERE int = 0 AND double < ' + numTestObjects/2 + ';');
|
|
|
|
let len = results.rows.length;
|
|
|
|
console.log("length: " + results.rows.length + " last: ", this.objectValues(results.rows.item(len - 1)));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async queryenum() {
|
|
|
|
await this.db.readTransaction(async (tx) => {
|
|
|
|
let [, results] = await tx.executeSql('SELECT * FROM t1 WHERE int = 0 AND double < ' + numTestObjects/2 + ';');
|
|
|
|
let len = results.rows.length;
|
|
|
|
var row;
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
row = results.rows.item(i);
|
|
|
|
row.int;
|
|
|
|
row.double;
|
|
|
|
row.string;
|
|
|
|
new Date(row.date);
|
|
|
|
}
|
|
|
|
console.log("length: " + len + " last: ", this.objectValues(row));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const tests = ["insertions", "binsertions", "enumeration", "querycount", "queryenum"];
|
|
|
|
const apiTests = [new RealmTests, new RNSqliteTests, new RNStoreTests];
|
|
|
|
|
|
|
|
class ReactNativeBenchmarks extends Component {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
dataSource: new ListView.DataSource({
|
|
|
|
rowHasChanged: (row1, row2) => row1 !== row2,
|
|
|
|
}),
|
|
|
|
running: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
this._renderRow = this._renderRow.bind(this);
|
|
|
|
this._runTests = this._runTests.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<View style={styles.container}>
|
|
|
|
<Text style={styles.title}>
|
|
|
|
ReactNative Storage Benchmarks
|
|
|
|
</Text>
|
|
|
|
<TouchableHighlight style={styles.button} onPress={this._runTests}>
|
|
|
|
<Text style={styles.buttonText}>
|
|
|
|
Run
|
|
|
|
</Text>
|
|
|
|
</TouchableHighlight>
|
|
|
|
<ListView contentContainerStyle={styles.list}
|
|
|
|
dataSource={this.state.dataSource}
|
|
|
|
renderRow={this._renderRow}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
_renderRow(rowData) {
|
|
|
|
return (
|
2016-02-15 22:12:26 +00:00
|
|
|
<Text style={styles.item}>{rowData.join('\t\t')}</Text>
|
2016-02-15 21:52:38 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
async _runTests() {
|
|
|
|
if (this.state.running) {
|
|
|
|
console.log("DISABLED");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState({running: true});
|
|
|
|
|
|
|
|
try {
|
|
|
|
await this._runTestsAsync();
|
|
|
|
} catch (e) {
|
|
|
|
console.error('Error running tests:', e);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState({running: false});
|
|
|
|
}
|
|
|
|
|
|
|
|
async _runTestsAsync() {
|
|
|
|
var data = [apiTests.map((api) => api.name)];
|
2016-02-15 22:12:26 +00:00
|
|
|
data[0].splice(0, 0, "\t\t");
|
2016-02-15 21:52:38 +00:00
|
|
|
for (let test of tests) {
|
|
|
|
data.push([test]);
|
|
|
|
this.setState({
|
|
|
|
dataSource: this.state.dataSource.cloneWithRows(data),
|
|
|
|
});
|
|
|
|
|
|
|
|
for (var apiTest of apiTests) {
|
|
|
|
var totalTime = 0;
|
|
|
|
console.log("Running " + apiTest.name + "." + test);
|
|
|
|
for (var i = 0; i < numRepeats; i++) {
|
|
|
|
await apiTest.setup(test);
|
|
|
|
|
|
|
|
var startTime = Date.now();
|
|
|
|
await apiTest[test]();
|
|
|
|
var endTime = Date.now();
|
|
|
|
|
|
|
|
var time = endTime - startTime
|
|
|
|
console.log("finished in " + time);
|
|
|
|
totalTime += time;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = data.slice();
|
|
|
|
let last = data.length-1;
|
|
|
|
data[last] = data[last].slice();
|
|
|
|
data[last].push(totalTime / numRepeats);
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
dataSource: this.state.dataSource.cloneWithRows(data),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
container: {
|
|
|
|
flex: 1,
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
backgroundColor: '#F5FCFF',
|
|
|
|
},
|
|
|
|
title: {
|
|
|
|
fontSize: 20,
|
|
|
|
textAlign: 'center',
|
|
|
|
margin: 20,
|
|
|
|
},
|
|
|
|
instructions: {
|
|
|
|
textAlign: 'center',
|
|
|
|
color: '#333333',
|
|
|
|
marginBottom: 5,
|
|
|
|
},
|
|
|
|
list: {
|
|
|
|
justifyContent: 'center',
|
|
|
|
flexDirection: 'column',
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
},
|
|
|
|
button: {
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
backgroundColor: '#CCC',
|
|
|
|
width: 100,
|
|
|
|
height: 40
|
|
|
|
},
|
|
|
|
row: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
},
|
|
|
|
item: {
|
|
|
|
textAlign: 'center',
|
|
|
|
padding: 3,
|
|
|
|
margin: 3,
|
|
|
|
fontSize: 12
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = ReactNativeBenchmarks;
|