Re: Home-brewed code is 100X faster than -[NSScanner scanDecimal:] ??
Re: Home-brewed code is 100X faster than -[NSScanner scanDecimal:] ??
- Subject: Re: Home-brewed code is 100X faster than -[NSScanner scanDecimal:] ??
- From: Matt Gough <email@hidden>
- Date: Thu, 3 Feb 2011 16:51:47 +0000
Probably because you aren't comparing like-for-like; did you try comparing it with [NSScanner scanDouble:] instead?
Your code wouldn't cope with the HUGE range of numbers that scanDecimal could be required to cope with, nor the variety of formats that NSDecimalNumber could scan.
Do you really need NSDecimalNumber support? Seems that doubles are fine for your needs.
Matt Gough
On 3 Feb 2011, at 16:35, Jerry Krinock wrote:
> -[NSScanner scanDecimal:] takes an average of 4 milliseconds to scan a short string of decimal digits, which means tens of seconds for thousands of scans, which is unacceptable for my application. Also, excessive memory allocations require a local autorelease pool around each invocation.
>
> Surprisingly, I was able to fix both problem by replacing -scanDecimal: with a rather bone-headed home-brew implementation, using -scanCharactersFromSet:intoString: instead. The home-brew implementation runs 100 to 150 times faster. How can this be?
>
> #import <Cocoa/Cocoa.h>
>
> @interface NSScanner (Speedy)
>
> - (BOOL)scanDecimalNumber:(NSNumber**)number ;
>
> @end
>
> @implementation NSScanner (Speedy)
>
> /*
> Bone-headed home-brew implementation
> */
> - (BOOL)scanDecimalNumber:(NSNumber**)number {
> NSString* string = nil ;
> NSCharacterSet* decimalSet ;
> decimalSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789-."] ;
> BOOL isDecimal = [self scanCharactersFromSet:decimalSet
> intoString:&string] ;
> if (isDecimal) {
> double value ;
> value = [string doubleValue] ; // See Note, below
> *number = [NSNumber numberWithDouble:value] ;
> }
>
> return isDecimal ;
>
> /*
> Note. At first, I'd written a more complicated implementation which checked
> if the number was an integer and if so used -numberWithInteger instead of
> -numberWithDouble, but then found that performance of the current
> implementation here is the same. I suppose this is because we've been
> doing floating-point calculations in hardware for quite a few years now.
> */
> }
>
> @end
>
> NSTimeInterval TestScanner(
> NSScanner *scanner,
> BOOL homeBrew,
> NSInteger exponent) {
> // We use a local autorelease pool, otherwise -scanDecimal: starts
> // using gigabytes of virtual memory, which makes both the HomeBrew
> // and scanDecimal methods take longer, and slow our Mac.
> NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ;
>
> [scanner setScanLocation:0] ;
> double checksum = 0.0 ;
> NSDate* date = [NSDate date] ;
> while (![scanner isAtEnd]) {
> NSNumber* number = nil ;
> BOOL didScanNumber ;
> if (homeBrew) {
> didScanNumber = [scanner scanDecimalNumber:&number] ;
> }
> else {
> NSDecimal decimal;
> didScanNumber = [scanner scanDecimal:&decimal];
> if (didScanNumber) {
> number = [NSDecimalNumber decimalNumberWithDecimal:decimal];
> }
> }
> if (didScanNumber) {
> double value = [number doubleValue] ;
> checksum += value ;
> }
> else {
> [scanner setScanLocation:[scanner scanLocation] + 1] ;
> }
> }
>
> /* The template includes four numbers: 1, -2, 3.5, -1.5. These
> numbers sum to 1. Therefore when we add together the sums
> of all the scanned numbers, the "checksum" should be equal
> to the number of times that the template appears in the
> scanner's string, which is 2^exponent. */
> BOOL checksumOK = checksum == pow(2,exponent) ;
>
> NSTimeInterval elapsed = -[date timeIntervalSinceNow] ;
> NSLog(@"%@: time = %9.3e seconds checksum = %0.16f (%@)",
> homeBrew ? @" Using Home-Brew" : @"Using -scanDecimal:",
> elapsed,
> checksum,
> checksumOK ? @"OK" : @"Error!") ;
> [pool release] ;
>
> return elapsed ;
> }
>
> #define HOME_BREW YES
> #define SCAN_DECIMAL NO
>
> int main(int argc, char *argv[]) {
> NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ;
>
> NSString* template = @"+int:1 -int:-2 +float:3.5 -float:-1.5 " ;
> NSMutableString* s = [template mutableCopy] ;
> NSInteger i ;
> NSInteger exponent = 9 ;
> // Repeat the string 2^exponent times
> for (i=0; i<exponent; i++) {
> [s appendString:s] ;
> }
>
> NSScanner* scanner = [[NSScanner alloc] initWithString:s] ;
>
> NSTimeInterval elapsedScanDecimal = 0.0 ;
> NSTimeInterval elapsedHomeBrew = 0.0 ;
>
> NSInteger j ;
> for (j=0; j<5; j++) {
> elapsedScanDecimal += TestScanner(scanner, SCAN_DECIMAL, exponent) ;
> elapsedHomeBrew += TestScanner(scanner, HOME_BREW, exponent) ;
> }
> NSLog (@"Speed Improvement HomeBrew/scanDecimal: %0.1f",
> elapsedScanDecimal/elapsedHomeBrew) ;
>
> [scanner release] ;
> [s release] ;
>
> [pool drain] ;
>
> return 0 ;
> }_______________________________________________
>
> Cocoa-dev mailing list (email@hidden)
>
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden