Skip to content

Instantly share code, notes, and snippets.

@markd2
Created March 21, 2013 03:00
Show Gist options
  • Save markd2/5210376 to your computer and use it in GitHub Desktop.
Save markd2/5210376 to your computer and use it in GitHub Desktop.
#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