Created
January 25, 2010 06:01
-
-
Save stuartcarnie/285663 to your computer and use it in GitHub Desktop.
Micro-benchmarks for iPhone and OS X
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PerfTester.h | |
// TollFreeBridingTests | |
// | |
// Created by Stuart Carnie on 1/24/10. | |
// Copyright 2010 Manomio. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@interface PerfTester : NSObject | |
{ | |
uint64_t mStartTime, mEndTime; | |
int mIterations; | |
} | |
- (void)test; | |
- (void)beginTest; | |
- (void)endTestWithIterations: (int)iters; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PerfTester.m | |
// TollFreeBridingTests | |
// | |
// Created by Stuart Carnie on 1/24/10. | |
// Copyright 2010 Manomio. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import <mach/mach_time.h> | |
#import <pthread.h> | |
#import "PerfTester.h" | |
@implementation PerfTester | |
struct Result | |
{ | |
int iterations; | |
double totalDuration; | |
double singleIterationNanosec; | |
}; | |
- (struct Result)_timeForSelector: (SEL)sel | |
{ | |
struct mach_timebase_info tbinfo; | |
mach_timebase_info( &tbinfo ); | |
mStartTime = mEndTime = 0; | |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |
[self performSelector: sel]; | |
[pool release]; | |
uint64_t duration = mEndTime - mStartTime; | |
double floatDuration = duration * tbinfo.numer / tbinfo.denom; | |
struct Result result = { | |
mIterations, | |
floatDuration / 1000000000.0, | |
floatDuration / mIterations | |
}; | |
return result; | |
} | |
int intRes; | |
double doubleRes; | |
double doubleDiv = 0; | |
- (void)test | |
{ | |
struct { NSString *name; SEL sel; } testSels[] = { | |
{ @"C++ virtual method call", @selector( testCPPVirtualCall ), }, | |
{ @"Objective-C message send", @selector( testMessaging ), }, | |
{ @"IMP-cached message send", @selector( testIMPCachedMessaging ), }, | |
//{ @"C++ cached virtual method call", @selector( testCPPCachedVirtualCall ), }, | |
{ @"NSInvocation message send", @selector( testNSInvocation ), }, | |
{ @"Integer division", @selector( testIntDivision ), }, | |
{ @"Floating-point division", @selector( testFloatDivision ), }, | |
{ @"Float division with int conversion", @selector( testFloatConversionDivision ), }, | |
{ @"NSObject alloc/init/release", @selector( testObjectCreation ), }, | |
{ @"NSAutoreleasePool alloc/init/release", @selector( testPoolCreation ), }, | |
{ @"16 byte malloc/free", @selector( testSmallMallocFree ), }, | |
{ @"16MB malloc/free", @selector( testLargeMallocFree ), }, | |
{ @"16 byte memcpy", @selector( testSmallMemcpy ), }, | |
{ @"1MB memcpy", @selector( testLargeMemcpy ), }, | |
//{ @"Write 16-byte file", @selector( testWriteSmallFile ), }, | |
//{ @"Write 16-byte file (atomic)", @selector( testWriteSmallFileAtomic ), }, | |
//{ @"Write 16MB file", @selector( testWriteLargeFile ), }, | |
//{ @"Write 16MB file (atomic)", @selector( testWriteLargeFileAtomic ), }, | |
//{ @"Read 16-byte file", @selector( testReadSmallFile ), }, | |
//{ @"Read 16MB file", @selector( testReadLargeFile ), }, | |
{ @"pthread create/join", @selector( testSpawnThread ), }, | |
{ @"Zero-second delayed perform", @selector( testDelayedPerform ), }, | |
//{ @"NSTask process spawn", @selector( testNSTask ) }, | |
{ @"Objective-C objectAtIndex:", @selector( testObjCObjectAtIndex), }, | |
{ @"CoreFoundation CFArrayGetValueAtIndex", @selector( testCFObjectAtIndex), }, | |
{ nil, NULL } | |
}; | |
doubleDiv = 42.3; | |
mIterations = 1000000000; | |
[self _timeForSelector: @selector( testNothing )]; | |
NSMutableArray *resultsArray = [NSMutableArray array]; | |
int i; | |
for( i = 0; testSels[i].name; i++ ) | |
{ | |
struct Result result = [self _timeForSelector: testSels[i].sel]; | |
struct Result overheadResult = [self _timeForSelector: @selector( testNothing )]; | |
double total = result.totalDuration - overheadResult.totalDuration; | |
double each = result.singleIterationNanosec - overheadResult.singleIterationNanosec; | |
NSDictionary *entry = [NSDictionary dictionaryWithObjectsAndKeys: | |
testSels[i].name, @"name", | |
[NSNumber numberWithInt: result.iterations], @"iterations", | |
[NSNumber numberWithDouble: total], @"total", | |
[NSNumber numberWithDouble: each], @"each", | |
nil]; | |
[resultsArray addObject: entry]; | |
NSLog( @"completed %@", testSels[i].name ); | |
} | |
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey: @"each" ascending: YES]; | |
[resultsArray sortUsingDescriptors: [NSArray arrayWithObject: descriptor]]; | |
[descriptor release]; | |
NSMutableString *str = [NSMutableString string]; | |
[str appendString: @"<table><tr><td>Name</td><td>Iterations</td><td>Total time (sec)</td><td>Time per (ns)</td></tr>"]; | |
NSEnumerator *enumerator = [resultsArray objectEnumerator]; | |
id obj; | |
while( (obj = [enumerator nextObject]) ) | |
{ | |
NSString *name = [obj objectForKey: @"name"]; | |
int iterations = [[obj objectForKey: @"iterations"] intValue]; | |
double total = [[obj objectForKey: @"total"] doubleValue]; | |
double each = [[obj objectForKey: @"each"] doubleValue]; | |
[str appendFormat: @"<tr><td>%@</td><td>%d</td><td>%.1f</td><td>%.1f</td></tr>", name, iterations, total, each]; | |
} | |
[str appendString: @"</table>\n"]; | |
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData: [str dataUsingEncoding: NSUTF8StringEncoding]]; | |
printf("%f\n", doubleRes); | |
} | |
- (void)beginTest | |
{ | |
mStartTime = mach_absolute_time(); | |
} | |
- (void)endTestWithIterations: (int)iters | |
{ | |
mEndTime = mach_absolute_time(); | |
mIterations = iters; | |
} | |
#define FAST_RUN 1 | |
#if FAST_RUN | |
#define k1000MMIterationTestCount 100000000 | |
#define k100MMIterationTestCount 10000000 | |
#define k10MMIterationTestCount 100000 | |
#define k100KIterationTestCount 1000 | |
#define k10KIterationTestCount 100 | |
#else | |
#define k1000MMIterationTestCount 1000000000 | |
#define k100MMIterationTestCount 100000000 | |
#define k10MMIterationTestCount 10000000 | |
#define k100KIterationTestCount 100000 | |
#define k10KIterationTestCount 10000 | |
#endif | |
#pragma mark - | |
/* | |
#define BEGIN( count ) \ | |
int iters = count; \ | |
int i; \ | |
[self beginTest]; \ | |
for( i = 1; i <= iters; i++ ) | |
*/ | |
#define BEGIN( count ) \ | |
int iters = count; \ | |
int i = count; \ | |
[self beginTest]; \ | |
while ( i-- ) | |
#define END() \ | |
[self endTestWithIterations: iters]; | |
- (void)testNothing | |
{ | |
BEGIN( mIterations ) | |
; | |
END() | |
} | |
class StubClass | |
{ | |
public: | |
virtual void stub() { } | |
}; | |
- (void)testCPPVirtualCall | |
{ | |
class StubClass *obj = new StubClass; | |
BEGIN( k1000MMIterationTestCount ) | |
obj->stub(); | |
END() | |
} | |
- (void)testCPPCachedVirtualCall { | |
class StubClass *obj = new StubClass; | |
void (StubClass::*fn)() = &StubClass::stub; | |
BEGIN( k1000MMIterationTestCount ) | |
(obj->*fn)(); | |
END() | |
} | |
- (void)_stubMethod | |
{ | |
} | |
- (void)testMessaging | |
{ | |
BEGIN( k1000MMIterationTestCount ) | |
[self _stubMethod]; | |
END() | |
} | |
- (void)testIMPCachedMessaging | |
{ | |
void (*imp)(id, SEL) = (void (*)(id, SEL))[self methodForSelector: @selector( _stubMethod )]; | |
BEGIN( k1000MMIterationTestCount ) | |
imp( self, @selector( _stubMethod ) ); | |
END() | |
} | |
- (void)testNSInvocation | |
{ | |
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: [self methodSignatureForSelector: @selector( _stubMethod )]]; | |
[invocation setSelector: @selector( _stubMethod )]; | |
[invocation setTarget: self]; | |
BEGIN( k10MMIterationTestCount ) | |
[invocation invoke]; | |
END() | |
} | |
- (void)testIntDivision | |
{ | |
int x; | |
BEGIN( k1000MMIterationTestCount ) | |
x = 1000000000 / i; | |
END() | |
intRes = x; | |
} | |
- (void)testFloatDivision | |
{ | |
double x = doubleDiv; | |
BEGIN( k100MMIterationTestCount ) | |
x = 100000000.0 / x; | |
END() | |
doubleRes = x; | |
} | |
- (void)testFloatConversionDivision | |
{ | |
double x; | |
BEGIN( k100MMIterationTestCount ) | |
x = 1000000000.0 / i; | |
END() | |
doubleRes = x; | |
} | |
- (void)testObjectCreation | |
{ | |
BEGIN( k10MMIterationTestCount ) | |
[[[NSObject alloc] init] release]; | |
END() | |
} | |
- (void)testPoolCreation | |
{ | |
BEGIN( k10MMIterationTestCount ) | |
[[[NSAutoreleasePool alloc] init] release]; | |
END() | |
} | |
- (void)testSmallMallocFree | |
{ | |
BEGIN( k100MMIterationTestCount ) | |
free( malloc( 16 ) ); | |
END() | |
} | |
- (void)testLargeMallocFree | |
{ | |
BEGIN( k100KIterationTestCount ) | |
free( malloc( 1 << 24 ) ); | |
END() | |
} | |
- (void)_testMemcpySize: (int)size count: (int)count | |
{ | |
void *src = malloc( size ); | |
void *dst = malloc( size ); | |
BEGIN( count ) | |
memcpy( dst, src, size ); | |
END() | |
free( src ); | |
free( dst ); | |
} | |
- (void)testSmallMemcpy | |
{ | |
[self _testMemcpySize: 16 count: k100MMIterationTestCount]; | |
} | |
- (void)testLargeMemcpy | |
{ | |
[self _testMemcpySize: 1 << 20 count: k10KIterationTestCount]; | |
} | |
- (void)_testWriteFileSize: (int)size atomic: (BOOL)atomic count: (int)count | |
{ | |
NSData *data = [[NSFileHandle fileHandleForReadingAtPath: @"/dev/random"] readDataOfLength: size]; | |
BEGIN( count ) | |
[data writeToFile: @"/tmp/testrand" atomically: atomic]; | |
END() | |
[[NSFileManager defaultManager] removeFileAtPath: @"/tmp/testrand" handler: nil]; | |
} | |
- (void)testWriteSmallFile | |
{ | |
[self _testWriteFileSize: 16 atomic: NO count: 10000]; | |
} | |
- (void)testWriteSmallFileAtomic | |
{ | |
[self _testWriteFileSize: 16 atomic: YES count: 10000]; | |
} | |
- (void)testWriteLargeFile | |
{ | |
[self _testWriteFileSize: 1 << 24 atomic: NO count: 30]; | |
} | |
- (void)testWriteLargeFileAtomic | |
{ | |
[self _testWriteFileSize: 1 << 24 atomic: YES count: 30]; | |
} | |
- (void)_testReadFileSize: (int)size count: (int)count | |
{ | |
NSData *data = [[NSFileHandle fileHandleForReadingAtPath: @"/dev/random"] readDataOfLength: size]; | |
[data writeToFile: @"/tmp/testrand" atomically: NO]; | |
BEGIN( count ) | |
[[[NSData alloc] initWithContentsOfFile: @"/tmp/testrand"] release]; | |
END() | |
[[NSFileManager defaultManager] removeFileAtPath: @"/tmp/testrand" handler: nil]; | |
} | |
- (void)testReadSmallFile | |
{ | |
[self _testReadFileSize: 16 count: 100000]; | |
} | |
- (void)testReadLargeFile | |
{ | |
[self _testReadFileSize: 1 << 24 count: 100]; | |
} | |
static void *stub_pthread( void * ) | |
{ | |
} | |
- (void)testSpawnThread | |
{ | |
BEGIN( k10KIterationTestCount ) | |
{ | |
pthread_t pt; | |
pthread_create( &pt, NULL, stub_pthread, NULL ); | |
pthread_join( pt, NULL ); | |
} | |
END() | |
} | |
- (void)_delayedPerform | |
{ | |
if( mIterations++ < k100KIterationTestCount ) | |
[self performSelector: @selector( _delayedPerform ) withObject: nil afterDelay: 0.0]; | |
else | |
CFRunLoopStop( CFRunLoopGetCurrent() ); | |
} | |
- (void)testDelayedPerform | |
{ | |
[self beginTest]; | |
[self performSelector: @selector( _delayedPerform ) withObject: nil afterDelay: 0.0]; | |
CFRunLoopRun(); | |
[self endTestWithIterations: k100KIterationTestCount]; | |
} | |
- (void)testNSTask | |
{ | |
/* BEGIN( 1000 ) | |
{ | |
NSTask *task = [[NSTask alloc] init]; | |
[task setLaunchPath: @"/usr/bin/false"]; | |
[task launch]; | |
[task waitUntilExit]; | |
[task release]; | |
} | |
END()*/ | |
} | |
- (void)testObjCObjectAtIndex { | |
NSArray* test = [NSArray arrayWithObjects:@"one", @"two", @"three", nil]; | |
BEGIN ( k100MMIterationTestCount ) | |
id obj = [test objectAtIndex:2]; | |
END() | |
} | |
- (void)testCFObjectAtIndex { | |
NSArray* test = [NSArray arrayWithObjects:@"one", @"two", @"three", nil]; | |
BEGIN ( k100MMIterationTestCount ) | |
id obj = (id)CFArrayGetValueAtIndex((CFArrayRef)test, 2); | |
END() | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment