291 lines
7.5 KiB
Objective-C
291 lines
7.5 KiB
Objective-C
#import "BenchmarkYapDatabase.h"
|
|
#import "YapDatabase.h"
|
|
#import "YapDatabaseConnection.h"
|
|
|
|
#import <stdlib.h>
|
|
|
|
@implementation BenchmarkYapDatabase
|
|
|
|
static YapDatabase *database;
|
|
static YapDatabaseConnection *connection;
|
|
|
|
static NSMutableArray *keys;
|
|
|
|
+ (NSString *)databaseName
|
|
{
|
|
return @"BenchmarkYapDatabase.sqlite";
|
|
}
|
|
|
|
+ (NSString *)databasePath
|
|
{
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
|
NSString *baseDir = ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory();
|
|
|
|
return [baseDir stringByAppendingPathComponent:[self databaseName]];
|
|
}
|
|
|
|
+ (NSString *)randomLetters:(NSUInteger)length
|
|
{
|
|
NSString *alphabet = @"abcdefghijklmnopqrstuvwxyz";
|
|
NSUInteger alphabetLength = [alphabet length];
|
|
|
|
NSMutableString *result = [NSMutableString stringWithCapacity:length];
|
|
|
|
NSUInteger i;
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
uint32_t randomIndex = arc4random_uniform((uint32_t)alphabetLength);
|
|
unichar c = [alphabet characterAtIndex:(NSUInteger)randomIndex];
|
|
|
|
[result appendFormat:@"%C", c];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
+ (void)generateKeys:(NSUInteger)count
|
|
{
|
|
if (keys == nil)
|
|
keys = [[NSMutableArray alloc] initWithCapacity:count];
|
|
else
|
|
[keys removeAllObjects];
|
|
|
|
for (NSUInteger i = 0; i < count; i++)
|
|
{
|
|
[keys addObject:[self randomLetters:24]];
|
|
}
|
|
}
|
|
|
|
+ (void)populateDatabase
|
|
{
|
|
NSDate *start = [NSDate date];
|
|
|
|
[connection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
for (NSString *key in keys)
|
|
{
|
|
// For now, use key for object.
|
|
// Later we need to test with bigger objects with more serialization overhead.
|
|
|
|
[transaction setObject:key forKey:key];
|
|
}
|
|
}];
|
|
|
|
NSTimeInterval elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
NSLog(@"Populate database: total time: %.6f, added items: %lu", elapsed, (unsigned long)[keys count]);
|
|
}
|
|
|
|
+ (void)enumerateDatabase
|
|
{
|
|
NSDate *start = [NSDate date];
|
|
|
|
[connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
[transaction enumerateKeys:^(NSString *key, BOOL *stop) {
|
|
|
|
// Nothing to do, just testing overhead
|
|
}];
|
|
}];
|
|
|
|
NSTimeInterval elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
NSLog(@"Enumerate keys: total time: %.6f, database count: %lu", elapsed, (unsigned long)[keys count]);
|
|
|
|
start = [NSDate date];
|
|
|
|
[connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
[transaction enumerateKeysAndObjectsUsingBlock:^(NSString *key, id object, id metadata, BOOL *stop) {
|
|
|
|
// Nothing to do, just testing overhead
|
|
}];
|
|
}];
|
|
|
|
elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
NSLog(@"Enumerate keys & objects: total time: %.6f, database count: %lu", elapsed, (unsigned long)[keys count]);
|
|
}
|
|
|
|
+ (void)fetchValuesInLoop:(NSUInteger)loopCount withCacheHitPercentage:(float)hitPercentage
|
|
{
|
|
// Generate a random list of keys to fetch which satisfies the requested hit percentage
|
|
|
|
NSUInteger cacheSize = connection.objectCacheLimit;
|
|
NSAssert(cacheSize < loopCount, @"Can't satisfy hitPercentage because cacheSize is too big");
|
|
|
|
NSMutableOrderedSet *keysInCache = [NSMutableOrderedSet orderedSetWithCapacity:cacheSize];
|
|
NSMutableArray *keysToFetch = [NSMutableArray arrayWithCapacity:loopCount];
|
|
|
|
for (NSUInteger i = 0; i < loopCount; i++)
|
|
{
|
|
float rand = arc4random_uniform(1000) / 1000.0F;
|
|
|
|
if ((rand < hitPercentage) && ([keysInCache count] > 0))
|
|
{
|
|
// Pick a random key in the cache
|
|
|
|
uint32_t randomIndex = arc4random_uniform((uint32_t)[keysInCache count]);
|
|
NSString *key = [keysInCache objectAtIndex:(NSUInteger)randomIndex];
|
|
|
|
[keysToFetch addObject:key];
|
|
|
|
[keysInCache removeObject:key];
|
|
[keysInCache insertObject:key atIndex:0];
|
|
}
|
|
else
|
|
{
|
|
// Pick a random key NOT in the cache
|
|
|
|
BOOL found = NO;
|
|
do {
|
|
|
|
uint32_t randomIndex = arc4random_uniform((uint32_t)[keys count]);
|
|
NSString *key = [keys objectAtIndex:(NSUInteger)randomIndex];
|
|
|
|
if (![keysInCache containsObject:key])
|
|
{
|
|
found = YES;
|
|
|
|
[keysToFetch addObject:key];
|
|
|
|
[keysInCache insertObject:key atIndex:0];
|
|
if ([keysInCache count] > cacheSize) {
|
|
[keysInCache removeObjectAtIndex:cacheSize];
|
|
}
|
|
}
|
|
|
|
} while (!found);
|
|
}
|
|
}
|
|
|
|
// Execute fetches
|
|
|
|
NSDate *start = [NSDate date];
|
|
|
|
[connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
for (NSString *key in keysToFetch)
|
|
{
|
|
(void)[transaction objectForKey:key];
|
|
}
|
|
}];
|
|
|
|
NSTimeInterval elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
|
|
NSLog(@"Fetch %lu random objs: total time: %.6f, average time per key: %.6f (cache hit %%: %.2f)",
|
|
(unsigned long)loopCount, elapsed, (elapsed / loopCount), hitPercentage);
|
|
}
|
|
|
|
+ (void)readTransactionOverhead:(NSUInteger)loopCount
|
|
{
|
|
NSDate *start = [NSDate date];
|
|
|
|
for (NSUInteger i = 0; i < loopCount; i++)
|
|
{
|
|
[connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
// Nothing to do, just testing overhead
|
|
}];
|
|
}
|
|
|
|
NSTimeInterval elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
NSLog(@"ReadOnly transaction overhead: %.8f", (elapsed / loopCount));
|
|
}
|
|
|
|
+ (void)readWriteTransactionOverhead:(NSUInteger)loopCount
|
|
{
|
|
NSDate *start = [NSDate date];
|
|
|
|
for (NSUInteger i = 0; i < loopCount; i++)
|
|
{
|
|
[connection readWriteWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
// Nothing to do, just testing overhead
|
|
}];
|
|
}
|
|
|
|
NSTimeInterval elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
NSLog(@"ReadWrite transaction overhead: %.8f", (elapsed / loopCount));
|
|
}
|
|
|
|
+ (void)removeAllValues
|
|
{
|
|
NSDate *start = [NSDate date];
|
|
|
|
[connection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
[transaction removeAllObjects];
|
|
}];
|
|
|
|
NSTimeInterval elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
NSLog(@"Remove all transaction: total time: %.6f", elapsed);
|
|
}
|
|
|
|
+ (void)startTests
|
|
{
|
|
NSString *databasePath = [self databasePath];
|
|
|
|
// Delete old database file (if exists)
|
|
[[NSFileManager defaultManager] removeItemAtPath:databasePath error:NULL];
|
|
|
|
// Create database
|
|
database = [[YapDatabase alloc] initWithPath:databasePath];
|
|
|
|
// Create database connection (can have multiple for concurrency)
|
|
connection = [database newConnection];
|
|
connection.objectCacheLimit = 250; // default size
|
|
|
|
// Setup
|
|
[self generateKeys:1000];
|
|
|
|
// Run tests
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@" \n\n\n ");
|
|
NSLog(@"====================================================");
|
|
NSLog(@"POPULATE DATABASE");
|
|
|
|
[self populateDatabase];
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@"ENUMERATE DATABASE");
|
|
|
|
[self enumerateDatabase];
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@"FETCH DATABASE");
|
|
|
|
[self fetchValuesInLoop:1000 withCacheHitPercentage:0.05];
|
|
[self fetchValuesInLoop:1000 withCacheHitPercentage:0.25];
|
|
[self fetchValuesInLoop:1000 withCacheHitPercentage:0.50];
|
|
[self fetchValuesInLoop:1000 withCacheHitPercentage:0.75];
|
|
[self fetchValuesInLoop:1000 withCacheHitPercentage:0.95];
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@"TRANSACTION OVERHEAD");
|
|
|
|
[self readTransactionOverhead:1000];
|
|
[self readWriteTransactionOverhead:1000];
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@"REMOVE ALL");
|
|
|
|
[self removeAllValues];
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
}
|
|
|
|
@end
|