なぜ文字列がそんなに遅いのですか?


23

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


11
これらの「平均的な操作」が神話的であることを知っている場合、少なくともそれらのいくつかを教えてもらえますか?あなたがそのようなあいまいな質問をしていることを考えると、これらの不特定の操作が本当に神話的であるというあなたの主張を信頼することは困難です。
SEH

1
@seh、残念ながら、私は実際にそれに答えることができません。実際に何文字列が遅いのかを人々に尋ねたとき、彼らはただ肩をすくめて、「彼らはただ遅い」と言いました。それに、もっと具体的な情報があれば、これはプログラマーではなくSOにとっての質問でしょう。それはすでにちょっと境界線です。
ポップス

ポイントは ?通知された文字列が実際に遅い場合、それらの使用を停止しますか?
Tulainsコルドバ

忘れてください。誰かがあなたにそのようなナンセンスを言うなら、反論は次のとおりです:「本当に?彼らですか?それからint配列を使うべきですか?」
インゴ

回答:


47

「平均的な操作」はプリミティブで行われます。しかし、文字列がプリミティブとして扱われる言語でさえ、それらは依然として内部の配列であり、文字列全体を含むすべての処理にはO(N)時間かかります。Nは文字列の長さです。

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

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


4
また、特定の言語では、配列の先頭で文字列の長さをDelphiでエンコードすると、文字列の連結が非常に高速になります。
フランクシェラー

4
@gablin:文字列のコピー自体を大幅に高速化することでも役立ちます。前もってサイズがわかっている場合は、一度に1バイトをコピーして各バイトにヌルターミネーターをチェックする必要はありません。したがって、SIMDを含む任意のレジスターのフルサイズを使用して、データの移動、最大16倍高速です。
メイソンウィーラー

4
@mathepic:ええ、それはあなたを連れて行く限りうまくいきますが、libcまたは他の外部コードとやり取りを始めるとき、それはa char*ではなくaを期待し、strbufあなたは正方形1に戻ります。悪いデザインが言語に組み込まれたときにできることです。
メイソンウィーラー

6
@mathepic:もちろんbufポインタはそこにあります。私はそれが利用できないことを意味するつもりはありませんでした。むしろ、それは必要です。標準ライブラリと 同じくらい基本的なものも含めて、最適化されているが非標準の文字列型を知らないコードは、まだ低速で安全ではありませんchar*。必要に応じてそのFUDを呼び出すことができますが、そうではありません。
メイソンウィーラー

7
人々、フランク・シアラーのポイントについてジョエル・スポルスキーのコラムがあります:基本に戻る
-user16764

14

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

構文糖衣は複雑さを隠します

文字列の問題は、ほとんどの言語で二流の市民であり、実際にはほとんどの場合、実際に言語仕様自体の一部ではないということです。使用する際の苦痛を軽減します。

これの直接的な結果は、言語が複雑さの大部分をあなたの視界から隠すことです。他のプリミティブ型(トップ投票の回答などで説明されています)。

実装の詳細

グッド・オル・アレイ

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

文字列全体へのアクセスを高速にしたいので、これは理にかなっています。ただし、この文字列を操作する場合は、恐ろしい費用がかかる可能性があります。後のインデックスを知っていれば、中央の要素にアクセスするのは速いかもしれませんが、条件に基づいて要素を探すのはそうではありません。

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

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

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

Unicodeサポート

また、これはあなたの言語の構文糖衣がこれをあなたから隠して素敵にプレイするという事実によるものです、あなたはしばしばそれをユニコードサポートの用語だとは思わないでしょう(特にあなたが本当にそれを必要としない限り)その壁にぶつかります)。また、いくつかの言語は先進的であるため、単純な8ビットcharプリミティブの基本配列を持つ文字列を実装しません。それらはUTF-8またはUTF-16で焼き付けられているか、何を持っているかをサポートします。その結果、非常に多くのメモリ消費が必要になりますが、メモリの割り当て、文字列の処理、コードポイントの操作と連動するすべてのロジックを実装します。


このすべての結果は、擬似コードで次のことと同等のことをすると:

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

それはそうではないかもしれません-言語開発者が彼らがあなたが望むように振る舞わせるために全力を尽くしたにもかかわらず-

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

フォローアップとして、以下をお読みください。


現在の議論への良い追加。
アベル

神話の声明はRSA暗号化が遅いなどのあらゆるものに適用できるため、これが最善の答えであることに気付きました。この恥ずかしいスポットに文字列が配置される唯一の理由は、ほとんどの言語の文字列にプラス演算子が用意されているためです。
コーディズム

@Abel:ありがとう、もっと一般的な詳細の余地があるように思えた。
ヘイレム

@Codism:ありがとう、気に入ってくれて嬉しい。私は確かに、これは複雑さが隠されているだけの問題である多くの場合に適用できると思います(そして、ボトルネックやある種のブリックウォールにぶつかって最終的に必要になるまで、下位レベルの詳細にあまり注意を払っていません)。
ヘイレム

1

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

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

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


1

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

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


0

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


2
必ずしもそうではありません。
user16764

3
Supercalifragilisticexpialidocious
スポーク

s / word / syllable / g
カレブ

あなたの質問と答えに質問で答えさせてください。あなたの答えが何を意味するのか、どうして言うのですか?結局のところ、それがどのようにランタイムシステムに適用されると解釈できるかは明らかではありません。
-PJTraill
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.