今審査中のアプリの話ですが、Case Insensitive(大文字小文字を無視)な文字列比較を結構な量実行してまして、最初はよくあるlowercaseStringを使った実装にしてたのですが、lowercaseStringを使わない実装に変えたところ体感できるレベル(と言ってもコンマ秒単位の話なんですけど)で実行速度が上がったのでご報告ということで。
まずlowercaseStringを使わないCase Insensitiveな文字列比較実装はこんな感じです。
#import <Foundation/Foundation.h> @interface NSString (Case) - (BOOL)caseInsensitiveHasPrefix:(NSString *)aString; - (BOOL)caseInsensitiveHasSuffix:(NSString *)aString; - (BOOL)caseInsensitiveIsEqualToString:(NSString *)aString; @end @implementation NSString (Case) - (BOOL)caseInsensitiveHasPrefix:(NSString *)aString { NSRange range = [self rangeOfString:aString options:NSCaseInsensitiveSearch|NSAnchoredSearch]; return (range.location != NSNotFound); } - (BOOL)caseInsensitiveHasSuffix:(NSString *)aString { NSRange range = [self rangeOfString:aString options:NSCaseInsensitiveSearch|NSBackwardsSearch|NSAnchoredSearch]; return (range.location != NSNotFound); } - (BOOL)caseInsensitiveIsEqualToString:(NSString *)aString { return ([self caseInsensitiveCompare:aString] == NSOrderedSame); } @end
caseInsensitiveCompareはFoundation.frameworkに元から含まれていますので、自前で用意したのは上の3つです。と言ってもcaseInsensitiveIsEqualToStringはcaseInsensitiveCompareを呼び出しているだけですが。
私のアプリではhasPrefixとhasSuffixを多用していましたので、caseInsensitiveHasPrefixとcaseInsensitiveHasSuffixが最適化に貢献してくれたようです。
今回ブログにアップするにあたり以下のコードで簡単なベンチマークも取ってみました。
NSString *str = @"AIUEO"; int i; NSLog(@"lowercaseString hasPrefix start"); for (i = 0; i < 100000; ++i) { if ([[str lowercaseString] hasPrefix:@"a"]) { } } NSLog(@"lowercaseString hasPrefix end"); NSLog(@"caseInsensitiveHasPrefix start"); for (i = 0; i < 100000; ++i) { if ([str caseInsensitiveHasPrefix:@"a"]) { } } NSLog(@"caseInsensitiveHasPrefix end"); NSLog(@"lowercaseString hasSuffix start"); for (i = 0; i < 100000; ++i) { if ([[str lowercaseString] hasSuffix:@"o"]) { } } NSLog(@"lowercaseString hasSuffix end"); NSLog(@"caseInsensitiveHasSuffix start"); for (i = 0; i < 100000; ++i) { if ([str caseInsensitiveHasSuffix:@"o"]) { } } NSLog(@"caseInsensitiveHasSuffix end"); NSLog(@"lowercaseString compare start"); for (i = 0; i < 100000; ++i) { if ([[str lowercaseString] compare:@"aiueo"] == NSOrderedSame) { } } NSLog(@"lowercaseString compare end"); NSLog(@"caseInsensitiveCompare start"); for (i = 0; i < 100000; ++i) { if ([str caseInsensitiveCompare:@"aiueo"] == NSOrderedSame) { } } NSLog(@"caseInsensitiveCompare end");
iPhone5での実行結果は以下のとおりです。
2013-03-04 11:42:57.464 CaseBanch[9638:907] lowercaseString hasPrefix start 2013-03-04 11:42:57.774 CaseBanch[9638:907] lowercaseString hasPrefix end 2013-03-04 11:42:57.775 CaseBanch[9638:907] caseInsensitiveHasPrefix start 2013-03-04 11:42:57.856 CaseBanch[9638:907] caseInsensitiveHasPrefix end 2013-03-04 11:42:57.857 CaseBanch[9638:907] lowercaseString hasSuffix start 2013-03-04 11:42:58.145 CaseBanch[9638:907] lowercaseString hasSuffix end 2013-03-04 11:42:58.146 CaseBanch[9638:907] caseInsensitiveHasSuffix start 2013-03-04 11:42:58.227 CaseBanch[9638:907] caseInsensitiveHasSuffix end 2013-03-04 11:42:58.228 CaseBanch[9638:907] lowercaseString compare start 2013-03-04 11:42:58.548 CaseBanch[9638:907] lowercaseString compare end 2013-03-04 11:42:58.549 CaseBanch[9638:907] caseInsensitiveCompare start 2013-03-04 11:42:58.590 CaseBanch[9638:907] caseInsensitiveCompare end
lowercaseString + hasPrefix | 0.310秒 |
caseInsensitiveHasPrefix | 0.081秒 |
lowercaseString + hasSuffix | 0.288秒 |
caseInsensitiveHasSuffix | 0.081秒 |
lowercaseString + compare | 0.320秒 |
caseInsensitiveCompare | 0.041秒 |