インタビュアーの元の質問には、「...そしてマルチスレッドが許可されています」と記載されています。この質問の言い回しは少し曖昧かもしれませんが、質問の趣旨は明らかです。面接担当者は候補者に、問題を解決するためのプログラムを作成し、マルチスレッドの使用 (または使用しない) を分析/正当化するよう求めています。提案されたソリューション。大規模な問題について考え、彼らが行うアルゴリズムの選択を説明する候補者の能力をテストするのは簡単な質問です。候補者がインターネットのウェブサイトから何かを理解せずに逆流させていないことを確認してください。
これを考えると、この特定のインタビューの質問は、マルチスレッドが使用されているかどうかに関係なく、O( n log n ) (漸近的に言えば) で効率的に解決でき、さらにマルチスレッドを使用して実際の実行時間を対数的に加速することができます。
ソリューションの概要
一流企業から OP の質問を受けた場合、次のアプローチは、問題と関連する問題を本当に理解していることを示します。ここでは、2 段階のアプローチを提案します。
ファイルは最初に分割され、メモリに読み込まれます。
マージ ソートの特別なバージョンがパーティションで使用され、ファイルがソートされているときに各名前の頻度を同時に集計します。
例として、それぞれ 1 文字の長さで、それぞれの初期頻度カウントが 1 である 32 個の名前を持つファイルを考えてみましょう。上記の戦略は、次のように視覚化できます。
1. File: ARBIKJLOSNUITDBSCPBNJDTLGMGHQMRH 32 Names
2. A|R|B|I|K|J|L|O|S|N|U|I|T|D|B|S|C|P|B|N|J|D|T|L|G|M|G|H|Q|M|R|H 32 Partitions
1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1 with counts
3. AR BI JK LO NS IU DT BS CP BN DJ LT GM GH MQ HR Merge #1
11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 and tally
4. ABRI JKLO INSU BDST BCNP DJLT GHM HMQR Merge #2
1111 1111 1111 1111 1111 1111 211 1111 and tally
5. ABIJKLOR BDINSTU BCDJLNPT GHMQR Merge #3
11111111 1111211 11111111 22211 and tally
6. ABDIJKLNORSTU BCDGHJLMNPQRT Merge #4
1212111111211 1112211211111 and tally
7. ABCDGHIJKLMNOPQRSTU Merge #5
1322111312132113121 and tally
したがって、メモリ内の最終的なリストを最初から最後まで読み取ると、並べ替えられたリストが得られます。
A|B|C|D|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
1|3|2|2|1|1|1|3|1|2|1|3|2|1|1|3|1|2|1 = 32 Name instances (== original file).
ソリューションが効率的な理由
ハッシュ テーブルが使用されているかどうか (元の投稿者が示唆したように)、およびマルチスレッドが使用されているかどうかにかかわらず、この問題に対する解決策は、並べ替えを実行する必要があるため、O( n log n )よりも効率的に解決できません。この制限があるため、採用できる戦略は 2 つあります。
ディスクからデータを読み取り、ハッシュ テーブルを使用して名前/頻度の合計を管理し、ハッシュ テーブルの内容を並べ替えます (元の投稿者が推奨する方法)
ディスクからデータを読み取り、ファイルからの頻度の合計で各名前を初期化し、名前をマージソートして、各名前のすべての合計を同時に合計します (このソリューション)。
解決策 (1) では、すべてのデータが読み込まれた後にハッシュ テーブルを並べ替える必要があります。解決策 (2) では、並べ替え中に頻度の集計を実行するため、ハッシュ テーブルのオーバーヘッドが取り除かれます。マルチスレッドをまったく考慮しなくても、解決策 (1) の最も効率的なハッシュ テーブルの実装を使用しても、解決策 (2) はハッシュ テーブルのオーバーヘッドがまったくないため、既により効率的であることがわかります。
マルチスレッドに関する制約
ソリューション (1) とソリューション (2) の両方で、これまでに考案された最も効率的なハッシュ テーブルの実装がソリューション (1) に使用されていると仮定すると、両方のアルゴリズムは O( n log n ) で漸近的に同じように実行されます。操作の順序が少し異なるだけです。ただし、ソリューション (1) をマルチスレッド化すると実際には実行が遅くなりますが、ソリューション (2) をマルチスレッド化すると速度が大幅に向上します。これはどのように可能ですか?
解決策 (1) をマルチスレッド化すると、ディスクからの読み取りまたはその後の並べ替えのいずれかで、すべてのスレッドが同時にハッシュ テーブルにアクセスしようとするため、ハッシュ テーブルで競合の問題が発生します。特にテーブルへの書き込みでは、この競合により解決策 (1)の実行時間が大幅に短縮される可能性があるため、マルチスレッドを使用せずに実行すると、実際には実行時間が短縮されます。
マルチスレッド化によって実行時間を高速化するには、各スレッドが実行する作業の各ブロックが他のすべてのスレッドから独立していることを確認する必要があります。これにより、すべてのスレッドが共有リソースで競合することなく最大速度で実行され、ジョブをはるかに高速に完了することができます。解決策 (2) はまさにこれを行い、ハッシュ テーブルを完全に削除し、問題を互いに独立したサブ問題に分割できる分割統治アルゴリズムであるMerge Sortを使用します。
実行時間をさらに改善するためのマルチスレッド化とパーティション化
マージソートをマルチスレッド化するために、ファイルをパーティションに分割し、新しいスレッドを作成して、連続するパーティションの各ペアをマージすることができます。ファイル内の名前は可変長であるため、パーティション分割を行うには、ファイルを最初から最後まで連続してスキャンする必要があります。ファイルへのランダム アクセスは使用できません。ただし、どのようなソリューションでも少なくとも 1 回はファイルの内容をスキャンする必要があるため、ファイルへのシリアル アクセスのみを許可することで最適なソリューションが得られます。
マルチスレッド化ソリューション(2)で期待できる実行時間の高速化はどのようなものですか? このアルゴリズムの分析は、その単純さを考えると非常にトリッキーであり、さまざまなホワイト ペーパーの主題となっています。ただし、ファイルをn 個のパーティションに分割すると、ファイルをパーティション分割しない単一の CPU よりも( n / log( n )) 倍速くプログラムを実行できます。簡単に言えば、単一のプロセッサが 640 GB のファイルを処理するのに 1 時間かかる場合、ファイルを 64 個の 10 GB のチャンクに分割し、32 個の CPU を搭載したマシンで実行すると、プログラムは約 6 分で完了することができ、10 倍の時間がかかります(ディスクを無視) 。諸経費)。