web-dev-qa-db-ja.com

なぜストリングはとても遅いのですか?

高校での最初のプログラミングクラス以来、文字列操作は神話的な「平均的な操作」よりも遅い、つまりコストがかかると聞いてきました。なぜそんなに遅くなるのですか? (この質問は意図的に広く残しました。)

23
Pops

「平均操作」はプリミティブで行われます。しかし、文字列がプリミティブとして扱われる言語でも、それらは内部で配列のままであり、文字列全体に関係することを行うと、O(N)時間かかります。ここで、Nは、ストリング。

たとえば、2つの数値を追加するには、通常2〜4個のASM命令が必要です。 2つの文字列を連結(「追加」)するには、新しいメモリ割り当てと、文字列全体を含む1つまたは2つの文字列コピーが必要です。

特定の言語要因が悪化する可能性があります。たとえば、Cでは、文字列はnullで終了する文字配列への単なるポインタです。つまり、その長さが分からないため、高速移動操作で文字列コピーループを最適化する方法はありません。ヌルターミネーターの各バイトをテストできるように、一度に1文字ずつコピーする必要があります。

47
Mason Wheeler

これは古いスレッドであり、他の答えは素晴らしいと思いますが、何かを見落としているので、ここでは(後期)2セントです。

構文糖衣は複雑さを隠す

文字列の問題は、ほとんどの言語で2番目のクラスの市民であり、実際にはほとんどの場合、実際には言語仕様自体の一部ではないということです。これらは、ライブラリ実装の構文であり、時々構文糖衣が上にあります。それらを使用するための苦痛を少なくするため。

これの直接の結果は、言語が複雑さの非常に大きな部分をあなたの視界から隠し、卑劣な副作用の代償を払うことです。なぜなら、それらを低レベルの原子エンティティのように考える習慣に成長するからです。その他のプリミティブ型(上位投票の回答などで説明されている)。

実装の詳細

グッド・オール・アレイ

この基本的な「複雑さ」の要素の1つは、ほとんどの文字列実装が、連続したメモリ空間を持つ単純なデータ構造を使用して文字列を表現することです。

ストリング全体へのアクセスを高速にしたいので、これは良い意味です。しかし、これは、この文字列を操作したい場合に恐ろしいコストがかかる可能性があることを意味します。後のインデックスがわかっていれば、途中の要素へのアクセスは速くなるかもしれませんが、条件に基づいて要素をlookingするのはそうではありません。

言語が文字列の長さをキャッシュせず、文字をカウントするために文字列を実行する必要がある場合、文字列のサイズを返すこともコストがかかる可能性があります。

同様の理由で、adding要素を文字列に追加すると、この操作を実行するためにメモリを再割り当てする必要が生じる可能性が高いため、コストがかかります。

したがって、言語によって、これらの問題に対するアプローチは異なります。たとえば、Javaは、いくつかの有効な理由(キャッシュの長さ、スレッドセーフ)のために文字列を不変にする自由をとり、その可変の対応物(StringBufferおよびStringBuilder)は、割り当てる必要のない大きなサイズのチャンクを使用してサイズを割り当てることを選択します毎回ですが、ベストケースのシナリオを期待しています。それは一般的にはうまく機能しますが、マイナス面は時々メモリへの影響を支払うことです。

Unicodeサポート

また、これはまた、あなたの言語の構文糖衣がニースをプレイするためにあなたからこれを隠しているという事実によるものです、あなたはしばしばそれをユニコードサポートの条件とは考えません(特にあなたが本当にそれを必要としない限り)そしてその壁を打つ)。また、一部の言語は、前向きに考えて、単純な8ビットcharプリミティブの基本配列を持つ文字列を実装していません。それらはUTF-8またはUTF-16またはあなたが何を持っているかのサポートで焼きました、そしてその結果はしばしば必要とされない途方もなく大きなメモリ消費と、メモリを割り当て、文字列を処理するためのより長い処理時間です、コードポイントの操作に関連するすべてのロジックを実装します。


このすべての結果は、擬似コードで同等の何かを実行すると、次のようになります。

hello = "hello,"
world = " world!"
str = hello + world

それはそうではないかもしれません-言語開発者があなたが思うようにそれらを振る舞わせるようにするすべての最善の努力にもかかわらず-次のように単純です:

a = 1;
b = 2;
shouldBeThree = a + b

フォローアップとして、あなたは読みたいかもしれません:

14
haylem

「平均操作」という語句は、理論上の ランダムアクセスストアドプログラムマシン の単一操作の略語と思われます。これは、さまざまなアルゴリズムの実行時間を分析するために通常使用する理論上のマシンです。

一般的な操作は、通常、ロード、追加、減算、保存、分岐であると見なされます。読んだり、印刷したり、停止したりすることもできます。

しかし、ほとんどの文字列操作には、これらの基本的な操作のいくつかが必要です。たとえば、文字列を複製するには、通常、コピー操作が必要です。したがって、文字列の長さに比例する(つまり、「線形」である)いくつかの操作が必要です。別の文字列内の部分文字列を検索する場合も、線形の複雑さがあります。

1
James Youngman

これは、操作、文字列の表現方法、および存在する最適化に完全に依存します。文字列の長さが4バイトまたは8バイトである(および整列されている)場合、必ずしも遅くなるとは限りません。多くの操作は、プリミティブと同じくらい高速です。または、すべての文字列が32ビットまたは64ビットのハッシュを持っている場合、多くの操作も同様に高速になります(ハッシュのコストを前払いします)。

また、「遅い」という意味にも依存します。ほとんどのプログラムは、必要なものに対して文字列をかなり高速に処理します。文字列の比較は2つのintの比較ほど高速ではないかもしれませんが、プロファイリングだけがプログラムにとって「遅い」という意味を明らかにします。

1
Kevin Hsu

質問に答えさせてください。一連の単語を言うと、1つの単語を言うよりも時間がかかるのはなぜですか?

0
ChaosPandion