#import #import #import #import "perf_iphone.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; } - (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 ), }, { @"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 ) }, { nil, NULL } }; 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: @""]; 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: @"", name, iterations, total, each]; } [str appendString: @"
NameIterationsTotal time (sec)Time per (ns)
%@%d%.1f%.1f
\n"]; [(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData: [str dataUsingEncoding: NSUTF8StringEncoding]]; } - (void)beginTest { mStartTime = mach_absolute_time(); } - (void)endTestWithIterations: (int)iters { mEndTime = mach_absolute_time(); mIterations = iters; } #pragma mark - #define BEGIN( count ) \ int iters = count; \ int i; \ [self beginTest]; \ for( i = 1; i <= iters; 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( 1000000000 ) obj->stub(); END() } - (void)_stubMethod { } - (void)testMessaging { BEGIN( 1000000000 ) [self _stubMethod]; END() } - (void)testIMPCachedMessaging { void (*imp)(id, SEL) = (void (*)(id, SEL))[self methodForSelector: @selector( _stubMethod )]; BEGIN( 1000000000 ) imp( self, @selector( _stubMethod ) ); END() } - (void)testNSInvocation { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: [self methodSignatureForSelector: @selector( _stubMethod )]]; [invocation setSelector: @selector( _stubMethod )]; [invocation setTarget: self]; BEGIN( 10000000 ) [invocation invoke]; END() } - (void)testIntDivision { int x; BEGIN( 1000000000 ) x = 1000000000 / i; END() } - (void)testFloatDivision { double x; double y = 42.3; BEGIN( 100000000 ) x = 100000000.0 / y; END() } - (void)testFloatConversionDivision { double x; BEGIN( 100000000 ) x = 1000000000.0 / i; END() } - (void)testObjectCreation { BEGIN( 10000000 ) [[[NSObject alloc] init] release]; END() } - (void)testPoolCreation { BEGIN( 10000000 ) [[[NSAutoreleasePool alloc] init] release]; END() } - (void)testSmallMallocFree { BEGIN( 100000000 ) free( malloc( 16 ) ); END() } - (void)testLargeMallocFree { BEGIN( 100000 ) 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: 100000000]; } - (void)testLargeMemcpy { [self _testMemcpySize: 1 << 20 count: 10000]; } - (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( 10000 ) { pthread_t pt; pthread_create( &pt, NULL, stub_pthread, NULL ); pthread_join( pt, NULL ); } END() } - (void)_delayedPerform { if( mIterations++ < 100000 ) [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: 100000]; } - (void)testNSTask { /* BEGIN( 1000 ) { NSTask *task = [[NSTask alloc] init]; [task setLaunchPath: @"/usr/bin/false"]; [task launch]; [task waitUntilExit]; [task release]; } END()*/ } @end