295 lines
7.8 KiB
Objective-C
295 lines
7.8 KiB
Objective-C
#import "BenchmarkYapCache.h"
|
|
#import "YapCache.h"
|
|
#import "YapCollectionKey.h"
|
|
|
|
#define LOOP_COUNT 25000
|
|
|
|
#define TEST_COLLECTION_KEY 1 // 0:Key=NSString, 1:Key=YapCollectionKey
|
|
|
|
|
|
/**
|
|
* Head-to-head stress test.
|
|
* We generate the exact same sequence of keys, and iterate over them as fast as possible.
|
|
**/
|
|
@implementation BenchmarkYapCache
|
|
|
|
static NSMutableArray *cacheSizes;
|
|
static NSMutableArray *keys;
|
|
|
|
+ (NSString *)randomLetters:(NSUInteger)length
|
|
{
|
|
NSString *alphabet = @"abcdefghijklmnopqrstuvwxyz";
|
|
NSUInteger alphabetLength = [alphabet length];
|
|
|
|
NSMutableString *result = [NSMutableString stringWithCapacity:length];
|
|
|
|
NSUInteger i;
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
unichar c = [alphabet characterAtIndex:(NSUInteger)arc4random_uniform((uint32_t)alphabetLength)];
|
|
|
|
[result appendFormat:@"%C", c];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
+ (void)generateKeysWithCacheSize:(NSUInteger)cacheSize targetHitPercentage:(double)hitPercentage
|
|
{
|
|
keys = [NSMutableArray arrayWithCapacity:LOOP_COUNT];
|
|
|
|
NSMutableArray *recentKeys = [NSMutableArray arrayWithCapacity:cacheSize];
|
|
|
|
for (NSUInteger i = 0; i < LOOP_COUNT; i++)
|
|
{
|
|
NSString *key;
|
|
|
|
if (arc4random_uniform(100) > (100 * hitPercentage) || [recentKeys count] == 0)
|
|
{
|
|
key = [self randomLetters:24];
|
|
|
|
[recentKeys addObject:key];
|
|
if ([recentKeys count] > cacheSize)
|
|
[recentKeys removeObjectAtIndex:0];
|
|
}
|
|
else
|
|
{
|
|
NSUInteger recentIndex = (NSUInteger)arc4random_uniform((uint32_t)[recentKeys count]);
|
|
|
|
key = [recentKeys objectAtIndex:recentIndex];
|
|
|
|
[recentKeys removeObjectAtIndex:recentIndex];
|
|
[recentKeys addObject:key];
|
|
}
|
|
|
|
#if TEST_COLLECTION_KEY
|
|
YapCollectionKey *ckey = [[YapCollectionKey alloc] initWithCollection:@"" key:key];
|
|
[keys addObject:ckey];
|
|
#else
|
|
[keys addObject:key];
|
|
#endif
|
|
}
|
|
}
|
|
|
|
+ (NSTimeInterval)testNSCache:(NSUInteger)cacheSize
|
|
{
|
|
NSCache *cache = [[NSCache alloc] init];
|
|
cache.countLimit = cacheSize;
|
|
|
|
NSUInteger hitCount = 0;
|
|
|
|
NSDate *start = [NSDate date];
|
|
|
|
for (id key in keys)
|
|
{
|
|
if ([cache objectForKey:key] == nil)
|
|
{
|
|
[cache setObject:[NSNull null] forKey:key];
|
|
}
|
|
else
|
|
{
|
|
hitCount++;
|
|
}
|
|
}
|
|
|
|
NSTimeInterval elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
double hitPercentage = (double)hitCount / (double)[keys count];
|
|
|
|
NSLog(@"NSCache : elapsed = %.6f (actual hit percentage = %.2f)", elapsed, hitPercentage);
|
|
|
|
return elapsed;
|
|
}
|
|
|
|
+ (NSTimeInterval)testYapCache:(NSUInteger)cacheSize
|
|
{
|
|
#if TEST_COLLECTION_KEY
|
|
YapCache *cache = [[YapCache alloc] initWithCountLimit:cacheSize keyCallbacks:[YapCollectionKey keyCallbacks]];
|
|
cache.allowedKeyClasses = [NSSet setWithObject:[YapCollectionKey class]];
|
|
#else
|
|
YapCache *cache = [[YapCache alloc] initWithCountLimit:cacheSize];
|
|
cache.allowedKeyClasses = [NSSet setWithObject:[NSString class]];
|
|
#endif
|
|
|
|
|
|
|
|
NSUInteger hitCount = 0;
|
|
|
|
NSDate *start = [NSDate date];
|
|
|
|
for (id key in keys)
|
|
{
|
|
if ([cache objectForKey:key] == nil)
|
|
{
|
|
[cache setObject:[NSNull null] forKey:key];
|
|
}
|
|
else
|
|
{
|
|
hitCount++;
|
|
}
|
|
}
|
|
|
|
NSTimeInterval elapsed = [start timeIntervalSinceNow] * -1.0;
|
|
double hitPercentage = (double)hitCount / (double)[keys count];
|
|
|
|
NSLog(@"YapCache: elapsed = %.6f (actual hit percentage = %.2f)", elapsed, hitPercentage);
|
|
|
|
return elapsed;
|
|
}
|
|
|
|
+ (void)testWithCompletion:(dispatch_block_t)completionBlock;
|
|
{
|
|
if ([cacheSizes count] == 0)
|
|
{
|
|
// Done!
|
|
completionBlock();
|
|
return;
|
|
}
|
|
|
|
NSUInteger cacheSize = [[cacheSizes objectAtIndex:0] unsignedIntegerValue];
|
|
[cacheSizes removeObjectAtIndex:0];
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@" \n\n\n ");
|
|
NSLog(@"====================================================");
|
|
NSLog(@"CACHE SIZE: %lu, TARGET HIT PERCENTAGE: 5%% \n\n", (unsigned long)cacheSize);
|
|
|
|
NSTimeInterval ns = 0.0;
|
|
NSTimeInterval yap = 0.0;
|
|
|
|
[self generateKeysWithCacheSize:cacheSize targetHitPercentage:0.05];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
|
|
ns = ns / 3.0;
|
|
yap = yap / 3.0;
|
|
|
|
if (ns < yap)
|
|
NSLog(@"Winner: NSCache (%.2f%% faster) \n ", ((1.0-(ns/yap))*100) );
|
|
else
|
|
NSLog(@"Winner: YapCache (%.2f%% faster) \n ", ((1.0-(yap/ns))*100) );
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@"CACHE SIZE: %lu, TARGET HIT PERCENTAGE: 25%% \n\n", (unsigned long)cacheSize);
|
|
|
|
NSTimeInterval ns = 0.0;
|
|
NSTimeInterval yap = 0.0;
|
|
|
|
[self generateKeysWithCacheSize:cacheSize targetHitPercentage:0.25];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
|
|
ns = ns / 3.0;
|
|
yap = yap / 3.0;
|
|
|
|
if (ns < yap)
|
|
NSLog(@"Winner: NSCache (%.2f%% faster) \n ", ((1.0-(ns/yap))*100) );
|
|
else
|
|
NSLog(@"Winner: YapCache (%.2f%% faster) \n ", ((1.0-(yap/ns))*100) );
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@"CACHE SIZE: %lu, TARGET HIT PERCENTAGE: 50%% \n\n", (unsigned long)cacheSize);
|
|
|
|
NSTimeInterval ns = 0.0;
|
|
NSTimeInterval yap = 0.0;
|
|
|
|
[self generateKeysWithCacheSize:cacheSize targetHitPercentage:0.5];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
|
|
ns = ns / 3.0;
|
|
yap = yap / 3.0;
|
|
|
|
if (ns < yap)
|
|
NSLog(@"Winner: NSCache (%.2f%% faster) \n ", ((1.0-(ns/yap))*100) );
|
|
else
|
|
NSLog(@"Winner: YapCache (%.2f%% faster) \n ", ((1.0-(yap/ns))*100) );
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@"CACHE SIZE: %lu, TARGET HIT PERCENTAGE: 75%% \n\n", (unsigned long)cacheSize);
|
|
|
|
NSTimeInterval ns = 0.0;
|
|
NSTimeInterval yap = 0.0;
|
|
|
|
[self generateKeysWithCacheSize:cacheSize targetHitPercentage:0.75];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
|
|
ns = ns / 3.0;
|
|
yap = yap / 3.0;
|
|
|
|
if (ns < yap)
|
|
NSLog(@"Winner: NSCache (%.2f%% faster) \n ", ((1.0-(ns/yap))*100) );
|
|
else
|
|
NSLog(@"Winner: YapCache (%.2f%% faster) \n ", ((1.0-(yap/ns))*100) );
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSLog(@"CACHE SIZE: %lu, TARGET HIT PERCENTAGE: 95%% \n\n", (unsigned long)cacheSize);
|
|
|
|
NSTimeInterval ns = 0.0;
|
|
NSTimeInterval yap = 0.0;
|
|
|
|
[self generateKeysWithCacheSize:cacheSize targetHitPercentage:0.95];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
ns += [self testNSCache:cacheSize];
|
|
yap += [self testYapCache:cacheSize];
|
|
|
|
ns = ns / 3.0;
|
|
yap = yap / 3.0;
|
|
|
|
if (ns < yap)
|
|
NSLog(@"Winner: NSCache (%.2f%% faster) \n ", ((1.0-(ns/yap))*100) );
|
|
else
|
|
NSLog(@"Winner: YapCache (%.2f%% faster) \n ", ((1.0-(yap/ns))*100) );
|
|
|
|
NSLog(@"====================================================");
|
|
});
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
// Run the next test (with a different cacheSize)
|
|
[self testWithCompletion:completionBlock];
|
|
});
|
|
}
|
|
|
|
+ (void)runTestsWithCompletion:(dispatch_block_t)completionBlock
|
|
{
|
|
// Run test for each of the cache sizes listed below
|
|
|
|
cacheSizes = [@[ @(40), @(100), @(500), @(1000), @(5000) ] mutableCopy];
|
|
[self testWithCompletion:completionBlock];
|
|
}
|
|
|
|
@end
|