Developer's Blog

【Objective-C】 文字列検索するなら、NSString の rangeOfString: より CFStringFind を使うべき(検証)

こんにちは、Sleipnir for Mac 開発担当の宮本です。

前回「Sleipnir for Mac のパフォーマンスを大幅に改善するためにしたこと」という記事で、rangeOfString: より CFStringFind を使ったほうがいいよと書きました。

今回は、実際にパフォーマンス計測してみてどのくらい違うかを調べてみます。

(追記)なぜパフォーマンスに違いがでてくるか判明したので、別の記事を書きました。
=> NSString#rangeOfString: と CFStringFind ではデフォルトのオプションが違う

計測について

  • ・環境: iMac (Mid 2011), 2.7GHz Intel Core i5
  • ・文字列の最後の5文字を探す
  • ・1万回の検索にかかる時間の10回平均
  • ・大文字小文字を区別するプションを引数で渡すかどうかで結果が違うのでそれぞれ計測
  • ・サンプルコードはに(検索文字列もここに)

結果

早速ですが、結果を発表します。

短い英語文字列 (54文字)

大文字小文字を区別 大文字小文字を区別しない
NS 0.00698954740 秒 0.00653741980 秒
CF 0.00341278890 秒 0.00616170650 秒
NS/CF

2.05 1.06

大文字小文字を区別する場合は、CFStringFind のほうが2倍も速いです。区別しない場合は、ほとんど変わらないようですね。

長い英語文字列 (368文字)

大文字小文字を区別 大文字小文字を区別しない
NS 0.04459974660 秒 0.04201309980 秒
CF 0.02190378760 秒 0.04105615600 秒
NS/CF 2.04 1.02

短い場合とあまり変わらないようです。

短い日本語文字列 (23文字)

大文字小文字を区別 大文字小文字を区別しない
NS 0.01534733860 秒 0.01673311200 秒
CF 0.00216053930 秒 0.01020237370 秒
NS/CF 7.10 1.64

なんということでしょう!
大文字小文字区別する場合は、CFStringFind が速すぎるというか、NSString#rangeOfString: が遅すぎます
区別しない場合でも、日本語なら 1.6 倍も違うので、CFStringFind を使ったほうが良さそうです。

長い日本語文字列 (134文字)

大文字小文字を区別 大文字小文字を区別しない
NS 0.08802418030 秒 0.09973368180 秒
CF 0.00990215510 秒 0.06137440920 秒
NS/CF 8.89 1.63

大文字小文字を区別する場合は、短い日本語文字列よりも差がつきました。ながければ長いほど開きがありそうです。

まとめ

  • ・NSString#rangeOfString: より CFStringFind のほうが速い
  • 英語(1バイト文字かな?) かつ 大文字小文字を区別しないならほとんど変わらない
  • 日本語のアプリを作ってて、大量の検索する場合は、CFStringFind を使うべき!

Sleipnir for Mac

Sleipnir for Macポータルフィールドでは、全履歴/全タブ/全ブックマークを CFStringFind で検索してます。ぜひ、お試しください!

Sleipnir for Mac はこちら

サンプルコード


#import <Foundation/Foundation.h>
#import <mach/mach_time.h>
#import <CoreFoundation/CoreFoundation.h>

void profileNSStringFind(NSString *string, NSStringCompareOptions options)
{
NSString *stringToFind = [string substringWithRange:NSMakeRange(string.length – 5, 5)];
for (int i = 0; i < 10000; i++) { [string rangeOfString:stringToFind options:options]; } } void profileCFStringFind(CFStringRef string, CFStringCompareFlags options) { CFStringRef stringToFind = CFStringCreateWithSubstring(NULL, string, CFRangeMake(CFStringGetLength(string) - 5, 5)); for (int i = 0; i < 10000; i++) { CFStringFind(string, stringToFind, options); } } void profileStringFind(NSString *string) { uint64_t start; mach_timebase_info_data_t base; mach_timebase_info(&base); double toSeconds = 1e-9 * ((double)base.numer) / ((double)base.denom); CGFloat nsResult = 0.0f; CGFloat cfResult = 0.0f; CGFloat nsResult_i = 0.0f; CGFloat cfResult_i = 0.0f; for (int i = 0; i < 10; i++) { start = mach_absolute_time(); profileNSStringFind(string, 0); nsResult += (mach_absolute_time() - start) * toSeconds; } nsResult /= 10.0f; for (int i = 0; i < 10; i++) { start = mach_absolute_time(); profileCFStringFind((CFStringRef)string, 0); cfResult += (mach_absolute_time() - start) * toSeconds; } cfResult /= 10.0f; for (int i = 0; i < 10; i++) { start = mach_absolute_time(); profileNSStringFind(string, NSCaseInsensitiveSearch); nsResult_i += (mach_absolute_time() - start) * toSeconds; } nsResult_i /= 10.0f; for (int i = 0; i < 10; i++) { start = mach_absolute_time(); profileCFStringFind((CFStringRef)string, kCFCompareCaseInsensitive); cfResult_i += (mach_absolute_time() - start) * toSeconds; } cfResult_i /= 10.0f; NSLog(@"NS: %.11f", nsResult); NSLog(@"CF: %.11f", cfResult); NSLog(@"NS(i): %.11f", nsResult_i); NSLog(@"CF(i): %.11f", cfResult_i); NSLog(@"CF が %.2f 倍速い", nsResult / cfResult); NSLog(@"CF(i) が %.2f 倍速い", nsResult_i / cfResult_i); } int main(int argc, const char * argv[]) { @autoreleasepool { NSString *shortText_ja = @"4文字打てばページが見つかるポータルフィールド"; NSString *longText_ja = @"ポータルフィールドなら、4文字も打てば即座に開きたいページを見つけ出せます。サイト単位で整理する独自のデザインで、過去のブラウザにありがちだった枝葉のような雑多なページを省き、短く見やすい候補リストを提示します。最小の入力で、思い描いたサイトに一直線にたどり着けます。"; NSString *shortText_en = @"Portal Field finds pages from just typing 4 characters"; NSString *longText_en = @"With Portal Field you can find the pages you want to open immediately after typing just 4 characters. The unorganized branched-out pages often found in past browsers have been scrapped for a compact easy-to-view list of suggestions uniquely designed to be sorted by site. You can use it to directly jump to the site you were thinking of with the least possible effort."; NSLog(@"== Short (ja): %d 文字", shortText_ja.length); profileStringFind(shortText_ja); NSLog(@"== Long (ja): %d 文字", longText_ja.length); profileStringFind(longText_ja); NSLog(@"== Short (en): %d 文字", shortText_en.length); profileStringFind(shortText_en); NSLog(@"== Long (en): %d 文字", longText_en.length); profileStringFind(longText_en); } return 0; } [/c]

Copyright © 2019 Fenrir Inc. All rights reserved.