1. ホーム
  2. c#

[解決済み】スタックサイズがデフォルトの50倍のスレッドを作成した場合の危険性とは?

2022-04-06 06:32:26

質問

私は現在、非常にパフォーマンスが重要なプログラムに取り組んでおり、リソースの消費を減らすのに役立つかもしれない1つの道を探ることにしたのは、ワーカスレッドのスタックサイズを増やして、ほとんどのデータを移動できるようにすることです ( float[] を使用して)スタックにアクセスすることになります。 stackalloc ).

私は 読む スレッドのデフォルトのスタックサイズは 1 MB であるため、私のすべての float[] のスタックを約50倍(50MB~)に拡張する必要があります。

これは一般的に "unsafe" と考えられており、推奨されないことは理解していますが、この方法に対して現在のコードをベンチマークした結果、私は 530% 処理速度が向上しました スタックを大きくすることの危険性(何が問題なのか)と、その危険性を最小化するための予防策を教えてください。

私のテストコード

public static unsafe void TestMethod1()
{
    float* samples = stackalloc float[12500000];

    for (var ii = 0; ii < 12500000; ii++)
    {
        samples[ii] = 32768;
    }
}

public static void TestMethod2()
{
    var samples = new float[12500000];

    for (var i = 0; i < 12500000; i++)
    {
        samples[i] = 32768;
    }
}

解決方法は?

テストコードをSamと比較したところ、どちらも正しいことが判明しました。

しかし、異なる点についてです。

  • メモリへのアクセス(読み出しと書き込み)は 同じ速さ スタック、グローバル、ヒープなど、どこであろうと。
  • アロケート しかし、スタック上では最も速く、ヒープ上では最も遅いです。

こんな感じです。 stack <です。 global < heap . (割り当て時間)

技術的には、スタックの割り当ては実際には割り当てではなく、ランタイムはスタックの一部(フレーム?)が配列用に予約されていることを確認するだけです。

これには気をつけることを強く勧めますが。

をお勧めします。

  1. のない配列を頻繁に作成する必要がある場合。 出る を使用することで、大幅に改善されます。
  2. 配列を再利用できるのであれば、できる限りそうしてください。ヒープはオブジェクトの長期保存に最適な場所です。(グローバルメモリを汚染するのは良いことではありません。スタックフレームが消える可能性があります)

( ノート : 1. 値の型にのみ適用されます。参照型はヒープに割り当てられ、その恩恵は0になります)

質問そのものにお答えします。私は、どの大容量スタックテストでも、全く問題に遭遇していません。

考えられる問題は、関数呼び出しに注意しないとスタックオーバーフローが発生することと、システムが低レベルで動作している場合にスレッドを作成する際にメモリ不足になることくらいだと考えています。

下のセクションは、私の最初の回答です。間違いっぽいし、テストも正しくない。参考程度にとどめておきます。


私のテストでは、スタックに割り当てられたメモリとグローバルメモリは、配列で使用する場合、ヒープに割り当てられたメモリよりも少なくとも15%遅い(120%の時間がかかる)ことが示されました!このような場合、スタックに割り当てられたメモリとグローバルメモリは、配列で使用する場合、ヒープに割り当てられたメモリよりも少なくとも15%遅い(120%の時間がかかる)。

これは私のテストコードです。 そして、これが出力例です。

Stack-allocated array time: 00:00:00.2224429
Globally-allocated array time: 00:00:00.2206767
Heap-allocated array time: 00:00:00.1842670
------------------------------------------
Fastest: Heap.

  |    S    |    G    |    H    |
--+---------+---------+---------+
S |    -    | 100.80 %| 120.72 %|
--+---------+---------+---------+
G |  99.21 %|    -    | 119.76 %|
--+---------+---------+---------+
H |  82.84 %|  83.50 %|    -    |
--+---------+---------+---------+
Rates are calculated by dividing the row's value to the column's.

Windows 8.1 Pro (Update 1)、i7 4700 MQ、.NET 4.5.1上でテストしました。

x86とx64の両方でテストしましたが、結果は同じです。

編集 : 全スレッドのスタックサイズを201MBに、サンプルサイズを5000万に、反復回数を5回にしました。

結果は以下の通りです。 上記と同じ :

Stack-allocated array time: 00:00:00.4504903
Globally-allocated array time: 00:00:00.4020328
Heap-allocated array time: 00:00:00.3439016
------------------------------------------
Fastest: Heap.

  |    S    |    G    |    H    |
--+---------+---------+---------+
S |    -    | 112.05 %| 130.99 %|
--+---------+---------+---------+
G |  89.24 %|    -    | 116.90 %|
--+---------+---------+---------+
H |  76.34 %|  85.54 %|    -    |
--+---------+---------+---------+
Rates are calculated by dividing the row's value to the column's.

とはいえ、実際にはスタックは 遅くなる .