Files
firebase-ios-sdk/Firestore/Example/Benchmarks/remote_document_cache_benchmark.mm
T
Gil f321ad85a9 Decode documents in a background thread (#4857)
* Add Index-free benchmarks

* Add BackgroundQueue

Port of firebase/firebase-android-sdk#559

* Decode documents in background threads to improve performance

Run on (12 X 2900 MHz CPU s)
CPU Caches:
  L1 Data 32K (x6)
  L1 Instruction 32K (x6)
  L2 Unified 262K (x6)
  L3 Unified 12582K (x1)
Load Average: 2.24, 2.18, 1.96
----------------------------------------------------------------------------------
Benchmark                           Time             CPU   Iterations  Improvement
----------------------------------------------------------------------------------
BM_QueryMatching/0/1              132 us         12.7 us        53524        21.4%
BM_QueryMatching/1/1              150 us         18.1 us        37305        16.2%
BM_QueryMatching/1/10             371 us         27.6 us        10000        48.5%
BM_QueryMatching/10/10            429 us         64.3 us        11349        41.0%
BM_QueryMatching/1/100           2573 us         29.0 us         1000        51.7% (max)
BM_QueryMatching/100/100         3123 us          416 us         1000        45.5%
BM_QueryMatching/1/1000         26785 us         33.5 us          100        50.5%
BM_QueryMatching/10/1000        28389 us         74.5 us          100        48.0%
BM_QueryMatching/100/1000       27883 us          470 us          100        49.4%
BM_QueryMatching/1000/1000      32189 us         34.3 us          100        44.8%
BM_QueryAll/1                     151 us         15.2 us        45385       -49.5% (min)
BM_QueryAll/10                    444 us         60.7 us        12829        32.5%
BM_QueryAll/100                  3175 us          414 us         1000        45.1%
BM_QueryAll/1000                30222 us         18.8 us          100        49.7%
Mean                                                                         35.3%

* Insert into a vector to reduce contention

Run on (12 X 2900 MHz CPU s)
CPU Caches:
  L1 Data 32K (x6)
  L1 Instruction 32K (x6)
  L2 Unified 262K (x6)
  L3 Unified 12582K (x1)
Load Average: 1.43, 2.10, 1.98
----------------------------------------------------------------------------------
Benchmark                           Time             CPU   Iterations  Improvement
----------------------------------------------------------------------------------
BM_QueryMatching/0/1              127 us         12.2 us        56131         3.8%
BM_QueryMatching/1/1              142 us         16.6 us        39200         5.3%
BM_QueryMatching/1/10             369 us         26.6 us        10000         0.5%
BM_QueryMatching/10/10            425 us         59.2 us        12724         0.9%
BM_QueryMatching/1/100           2194 us         28.9 us        10000        14.7%
BM_QueryMatching/100/100         2765 us          405 us         1000        11.5%
BM_QueryMatching/1/1000         21095 us         32.8 us         1000        21.2%
BM_QueryMatching/10/1000        20797 us         66.4 us         1000        26.7% (max)
BM_QueryMatching/100/1000       21915 us          408 us         1000        21.4%
BM_QueryMatching/1000/1000      23700 us         30.9 us         1000        26.4%
BM_QueryAll/1                     136 us         14.3 us        44799         9.9%
BM_QueryAll/10                    443 us         57.3 us        10000         0.2% (min)
BM_QueryAll/100                  2607 us          389 us         1000        17.9%
BM_QueryAll/1000                23512 us         19.4 us         1000        22.2%
Mean                                                                         13.1%
2020-02-15 11:09:25 -08:00

197 lines
6.4 KiB
Plaintext

/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <FirebaseCore/FIRApp.h>
#import <FirebaseFirestore/FirebaseFirestore.h>
#include "Firestore/core/src/firebase/firestore/util/autoid.h"
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
#include "Firestore/core/test/firebase/firestore/testutil/app_testing.h"
#include "benchmark/benchmark.h"
namespace {
using firebase::firestore::testutil::AppForUnitTesting;
using firebase::firestore::util::CreateAutoId;
using firebase::firestore::util::MakeNSString;
using firebase::firestore::util::MakeString;
FIRFirestore* OpenFirestore() {
FIRApp* app = AppForUnitTesting();
auto db = [FIRFirestore firestoreForApp:app];
auto settings = db.settings;
// Default to running against the emulator because we're evaluating local execution speed.
settings.host = @"localhost:8080";
settings.sslEnabled = NO;
// The default is the main queue and this deadlocks because the benchmark does not start an event
// loop and just runs on the main thread.
settings.dispatchQueue = dispatch_queue_create("results", DISPATCH_QUEUE_SERIAL);
db.settings = settings;
return db;
}
NSMutableDictionary<NSString*, id>* MakeDocumentData() {
NSMutableDictionary<NSString*, id>* doc = [[NSMutableDictionary alloc] init];
NSString* value = MakeNSString(std::string('a', 100));
// Create keys "a", "b", "c", ..., "j", each associated with the 100 byte
// value. This makes the total document size ~1 kb.
std::string key_bytes("a");
for (int i = 0; i < 10; i++) {
key_bytes[0] = static_cast<char>('a' + i);
doc[MakeNSString(key_bytes)] = value;
}
return doc;
}
FIRQuerySnapshot* GetDocumentsFromCache(FIRQuery* query) {
__block FIRQuerySnapshot* result;
dispatch_semaphore_t done = dispatch_semaphore_create(0);
[query getDocumentsWithSource:FIRFirestoreSourceCache
completion:^(FIRQuerySnapshot* snap, NSError* error) {
HARD_ASSERT(error == nil, "Failed: %s", MakeString([error description]));
result = snap;
dispatch_semaphore_signal(done);
}];
dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
return result;
}
FIRQuerySnapshot* GetDocumentsFromServer(FIRQuery* query) {
__block FIRQuerySnapshot* result;
dispatch_semaphore_t done = dispatch_semaphore_create(0);
[query getDocumentsWithSource:FIRFirestoreSourceServer
completion:^(FIRQuerySnapshot* snap, NSError* error) {
HARD_ASSERT(error == nil, "Failed: %s", MakeString([error description]));
result = snap;
dispatch_semaphore_signal(done);
}];
dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
return result;
}
void WaitForPendingWrites(FIRFirestore* db) {
dispatch_semaphore_t done = dispatch_semaphore_create(0);
[db waitForPendingWritesWithCompletion:^(NSError* error) {
dispatch_semaphore_signal(done);
}];
dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
}
void WriteDocs(FIRCollectionReference* collection, int64_t count, bool match) {
auto doc = MakeDocumentData();
for (int64_t i = 0; i < count; i++) {
doc[@"match"] = @(match);
FIRDocumentReference* ref = [collection documentWithAutoID];
[ref setData:doc];
}
WaitForPendingWrites(collection.firestore);
}
void Shutdown(FIRFirestore* db) {
dispatch_semaphore_t done = dispatch_semaphore_create(0);
[db terminateWithCompletion:^(NSError* error) {
dispatch_semaphore_signal(done);
}];
dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
}
void BM_QueryIndexFree(benchmark::State& state) {
int64_t matching_docs = state.range(0);
int64_t total_docs = state.range(1);
FIRFirestore* db = OpenFirestore();
auto collection = [db collectionWithPath:MakeNSString("docs-" + CreateAutoId())];
WriteDocs(collection, matching_docs, /*match=*/true);
WriteDocs(collection, total_docs - matching_docs, /*match=*/false);
FIRQuery* query = [collection queryWhereField:@"match" isEqualTo:@YES];
// Query the server to force the target tables to be updated.
GetDocumentsFromServer(query);
for (auto _ : state) {
auto docs = GetDocumentsFromCache(query);
(void)docs;
}
Shutdown(db);
}
BENCHMARK(BM_QueryIndexFree)
->Unit(benchmark::kMicrosecond)
->Args({0, 1})
->Args({1, 1})
->Args({1, 10})
->Args({10, 10})
->Args({1, 100})
->Args({100, 100})
->Args({1, 1000})
->Args({10, 1000})
->Args({100, 1000})
->Args({1000, 1000});
void BM_QueryMatching(benchmark::State& state) {
int64_t matching_docs = state.range(0);
int64_t total_docs = state.range(1);
FIRFirestore* db = OpenFirestore();
auto collection = [db collectionWithPath:MakeNSString("docs-" + CreateAutoId())];
WriteDocs(collection, matching_docs, /*match=*/true);
WriteDocs(collection, total_docs - matching_docs, /*match=*/false);
for (auto _ : state) {
auto docs = GetDocumentsFromCache([collection queryWhereField:@"match" isEqualTo:@YES]);
(void)docs;
}
Shutdown(db);
}
BENCHMARK(BM_QueryMatching)
->Unit(benchmark::kMicrosecond)
->Args({0, 1})
->Args({1, 1})
->Args({1, 10})
->Args({10, 10})
->Args({1, 100})
->Args({100, 100})
->Args({1, 1000})
->Args({10, 1000})
->Args({100, 1000})
->Args({1000, 1000});
void BM_QueryAll(benchmark::State& state) {
int64_t total_docs = state.range(0);
FIRFirestore* db = OpenFirestore();
auto collection = [db collectionWithPath:MakeNSString("docs-" + CreateAutoId())];
WriteDocs(collection, total_docs, /*match=*/true);
for (auto _ : state) {
auto docs = GetDocumentsFromCache(collection);
(void)docs;
}
Shutdown(db);
}
BENCHMARK(BM_QueryAll)->Unit(benchmark::kMicrosecond)->Arg(1)->Arg(10)->Arg(100)->Arg(1000);
} // namespace