Skip to content

Instantly share code, notes, and snippets.

@stuartcarnie
Created January 25, 2010 06:01
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stuartcarnie/285663 to your computer and use it in GitHub Desktop.
Save stuartcarnie/285663 to your computer and use it in GitHub Desktop.
Micro-benchmarks for iPhone and OS X
//
// 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
//
// 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