Created
March 21, 2013 03:00
-
-
Save markd2/5210376 to your computer and use it in GitHub Desktop.
This file contains 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
#import <Foundation/Foundation.h> | |
#import <mach/mach_time.h> // for mach_absolute_time() and friends | |
// clang -g -fobjc-arc -Weverything -Wno-unused-parameter -framework Foundation -o iteration iteration.m | |
// Run this by passing arguments on the command line. Run without any | |
// arguments to see the supported flags. Each time a flag is used causes | |
// that test to be run and timed, with the time (in seconds) output when | |
// it finishes. Be careful not to do anything else on your machine (such | |
// as surfing Redding while getting bored looking at the terminal) otherwise | |
// you'll skew the results. | |
// For example, this will run the object at index, fast enumeration, and | |
// parallel block three times, running the tests in a round-robin fashion: | |
// % ./iteration -ofbofbofb | |
// On my 2010 MBP, this takes < 1 second | |
// #define ARRAY_SIZE 5000000 | |
// This takes around 1.5 seconds at the most | |
#define ARRAY_SIZE 50000000 | |
// -------------------------------------------------- | |
// Each of these functions iterates through the array in different ways, | |
// doing a minimal amount of work - just enough to convince me the | |
// function actually ran. | |
static void UseObjectAtIndex (NSArray *array) { | |
NSUInteger count = 0; | |
for (NSUInteger i = 0; i < array.count; i++) { | |
NSNumber *number = [array objectAtIndex: i]; | |
if (number != nil) count++; | |
} | |
assert (count == array.count); | |
} // UseObjectAtIndex | |
static void UseObjectAtIndexWithHoistedCount (NSArray *array) { | |
NSUInteger count = 0; | |
NSUInteger arrayCount = array.count; | |
for (NSUInteger i = 0; i < arrayCount; i++) { | |
NSNumber *number = [array objectAtIndex: i]; | |
if (number != nil) count++; | |
} | |
assert (count == array.count); | |
} // UseObjectAtIndexWithHoistedCount | |
static void UseNSEnumerator (NSArray *array) { | |
NSUInteger count = 0; | |
NSEnumerator *enumerator = [array objectEnumerator]; | |
NSNumber *number; | |
while ((number = [enumerator nextObject])) { | |
if (number != nil) count++; | |
} | |
assert (count == array.count); | |
} // UseNSEnumerator | |
static void UseFastEnumeration (NSArray *array) { | |
NSUInteger count = 0; | |
for (NSNumber *number in array) { | |
if (number != nil) count++; | |
} | |
assert (count == array.count); | |
} // UseFastEnumeration | |
static void UseBlock (NSArray *array) { | |
__block NSUInteger count = 0; | |
[array enumerateObjectsUsingBlock: ^(NSNumber *number, NSUInteger index, BOOL *stop) { | |
if (number != nil) count++; | |
}]; | |
assert (count == array.count); | |
} // UseBlock | |
static void UseBlockParallel (NSArray *array) { | |
__block NSUInteger count = 0; | |
[array enumerateObjectsWithOptions: NSEnumerationConcurrent | |
usingBlock: ^(NSNumber *number, NSUInteger index, BOOL *stop) { | |
if (number != nil) count++; | |
}]; | |
// |count| has a bad value beacause of threading reasons, but that's | |
// ok for this test, of figuring out what the iteration overhed is. | |
// assert (count == array.count); | |
} // UseBlockParallel | |
// -------------------------------------------------- | |
// My favorite timing utility. | |
// http://blog.bignerdranch.com/316-a-timing-utility/ | |
static CGFloat BNRTimeBlock (void (^block)(void)) { | |
mach_timebase_info_data_t info; | |
if (mach_timebase_info(&info) != KERN_SUCCESS) return -1.0; | |
uint64_t start = mach_absolute_time (); | |
block (); | |
uint64_t end = mach_absolute_time (); | |
uint64_t elapsed = end - start; | |
uint64_t nanos = elapsed * info.numer / info.denom; | |
return (CGFloat)nanos / NSEC_PER_SEC; | |
} // BNRTimeBlock | |
static __attribute__((noreturn)) void Usage (void) { | |
printf ("Play with different forms of NSArray iteration. You can stack the arguments:\n"); | |
printf (" -o -- use objectAtIndex:\n"); | |
printf (" -h -- use objectAtIndex: with hoisted count\n"); | |
printf (" -e -- use NSEnumerator\n"); | |
printf (" -f -- use fast enumeration\n"); | |
printf (" -b -- use blocks\n"); | |
printf (" -p -- use blocks, parallel\n"); | |
exit (0); | |
} // Usage | |
// Macro to wrap an expression in a block and time it, and print out the | |
// result (time in seconds). | |
#define TIME(x) do { \ | |
CGFloat time = BNRTimeBlock( ^{ (x); } ); \ | |
printf ("done in %0.4f seconds\n", time); \ | |
} while (0) | |
int main (int argc, char * const argv[]) { | |
if (argc == 1) Usage (); | |
@autoreleasepool { | |
// Build the array. | |
NSMutableArray *builder = [NSMutableArray array]; | |
for (NSUInteger i = 0; i < ARRAY_SIZE; i++) { | |
[builder addObject: @(i)]; | |
} | |
// Strip off mutability. | |
NSArray *array = [builder copy]; | |
int opt; | |
while ((opt = getopt(argc, argv, "ohefbp")) != -1) { | |
switch (opt) { | |
case 'o': | |
printf ("running object at index...\n"); | |
TIME( UseObjectAtIndex (array) ); | |
break; | |
case 'h': | |
printf ("running object at index with hoisted count...\n"); | |
TIME( UseObjectAtIndexWithHoistedCount (array) ); | |
break; | |
case 'e': | |
printf ("running NSEnumerator...\n"); | |
TIME( UseNSEnumerator (array) ); | |
break; | |
case 'f': | |
printf ("running fast enumeration...\n"); | |
TIME( UseFastEnumeration (array) ); | |
break; | |
case 'b': | |
printf ("running block...\n"); | |
TIME( UseBlock (array) ); | |
break; | |
case 'p': | |
printf ("running block parallel...\n"); | |
TIME( UseBlockParallel (array) ); | |
break; | |
case '?': | |
default: | |
Usage (); | |
} | |
} | |
} | |
return 0; | |
} // main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment