Developer's Blog

NSString#rangeOfString: と CFStringFind ではデフォルトのオプションが違う

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

先ほど、CFStringFind を使うと文字列検索が速いよという記事を書いたのですが、速さの違いはオプションによるもののようです。
Twitter での、開発者さんたちの反応でわかりました。勉強になります!

rangeOfString:options: には NSLiteralSearch というオプションが、CFStringFindWithOptions には kCFCompareNonliteral というオプションがあります。

それぞれ逆のことを意味するオプションがあって、デフォルト値が逆になっているわけです。

(rangeOfString:Options:) + (NSLiteralSearch) = (CFStringFind)
(rangeOfString:) = (CFStringFindWithOptions) + (kCFCompareNonliteral)

といった感じでしょうか。

NSLiteralSearch を指定したらなぜ速いのか

日本語であれば、濁点/半濁点付の文字で問題があります。
「が」という文字は、「が」単体と、(「か」と「゛(濁点)」)との組み合わせで表現する合成文字の2種類が存在します。
NSString の rangeOfString: は、デフォルトでこの2種類を同等のものとして検索してくれます。一方、NSLiteralSearch を指定するか、CFStringFind を使うと byte ごとに比較するようになり、別のものとして扱われます。この違いだけで、8倍もの差が生まれてしまうわけです。

日本語だけでなく、ウムラウト記号が付く場合にもこの影響を受けます。リファレンスで kCFCompareNonliteral の説明に書いてあります。

オプションを合わせたときのパフォーマンスの違い

確認してみると、CFStringFind のほうが 1.05倍ほど速いぐらいの違いはありますが、ほとんど同じパフォーマンスです。

大文字小文字を区別、かつ非リテラルサーチをする場合は、あまりにもパフォーマンスに違いがあります。
パフォーマンスが重要な箇所では、合成文字を無視して NSLiteralSearch してしまうのも一つの手ですね。

まとめ

  • ・NSLiteralSearch かどうかで 8 倍ほどの違いがでてしまう
  • ・パフォーマンスが重要な場合は、合成文字を無視するのもアリ
  • ・オプションはよく調べよう!!

Copyright © 2019 Fenrir Inc. All rights reserved.