13

したがって、C++では非常に簡単です。クラス/構造体をヒープに割り当てる場合は、newを使用します。スタックに配置したい場合は、newを使用しないでください。

C#では、常に新しいキーワードを使用します。これが構造体であるかクラスであるかに応じて、スタックまたはヒープのいずれかに割り当てられます(構造体はスタックに、クラスはヒープに割り当てられます)。一部のアプリケーションでは、それらのオブジェクトだけが実際にそこに属するヒープに移動するように設計を変更すると、パフォーマンスに大きな違いが生じます。

私が疑問に思うのは、オブジェクトが構造体として宣言されているかクラスとして宣言されているかに関係なく、オブジェクトが割り当てられる場所を直接制御する方法はありますか?値型(構造体)をボックス化してヒープに移動できることは知っています(ただし、ボックス化/ボックス化解除にはパフォーマンスが犠牲になります)。スタックにクラスを割り当てる方法はありますか?

また、生のメモリを割り当てて、C ++の新しい配置のようなものを使用するメカニズムはありますか?これは管理の考え方に反することは知っていますが、カスタムメモリ管理を使用できる場合は、パフォーマンスに大きな違いが生じる可能性があります。

ガベージコレクターなどの便利さからC#が大好きですが、アプリケーションのボトルネックに取り組むときは、実際に何が起こっているかをより細かく制御することが望ましい場合があります。

ヒント/ヒントは大歓迎です:)

編集:パフォーマンスの例:

struct Foo1
{
    public int i;
    public float f;
    public double d;
}

struct Foo2
{
   public Foo1[] bar;

   public void Init(){
        bar = new Foo1[100];
        for (int i = 0; i < 100; i++)
            bar[i] = new Foo1();
    }
}

class Program
{
    static void Main(string[] args)
    {
        DateTime time = DateTime.Now;
        Foo2[] arr = new Foo2[1000000];
        for (int i = 0; i < 1000000; i++)
        {
            arr[i] = new Foo2();
            arr[i].Init();
        }

        Console.WriteLine((DateTime.Now - time).TotalMilliseconds);
    }
}

これは私のマシンで実行するのに1.8秒かかります(実際には割り当てのみが行われていることに注意してください-パラメーターの受け渡しはありません)

Foo1が構造体からクラスに変更された場合、実行には8.9秒かかります。それは5倍遅いです

4

5 に答える 5

10

一般的なケースでは、オブジェクトは常にヒープに割り当てられますが、C#を使用すると、相互運用性が高い場合やパフォーマンスが非常に重要なコードの場合は、ポインターレベルにドロップダウンできます。

安全でないブロックでは、stackalloc使用してスタックにオブジェクトを割り当て、それらをポインターとして使用できます。

彼らの例を引用するには:

// cs_keyword_stackalloc.cs
// compile with: /unsafe
using System; 

class Test
{
   public static unsafe void Main() 
   {
      int* fib = stackalloc int[100];
      int* p = fib;
      *p++ = *p++ = 1;
      for (int i=2; i<100; ++i, ++p)
         *p = p[-1] + p[-2];
      for (int i=0; i<10; ++i)
         Console.WriteLine (fib[i]);
   }
}

ただし、メソッド全体を安全でないと宣言する必要はなく、unsafe {...}ブロックを使用するだけでよいことに注意してください。

于 2010-01-08T22:48:44.937 に答える
6

値型と参照型の行き先(スタックとヒープ)の説明は完全には正しくありません。

構造体は、たとえば参照型のメンバーである場合、ヒープに割り当てられることもあります。または、オブジェクト参照を介してそれらを渡すときにボックス化した場合。

さまざまなタイプが実際に割り当てられている場所をよりよく理解するには、http://www.yoda.arachsys.com/csharp/memory.htmlを読む必要があります。

別の注意点として、.Netでは、型がどこに割り当てられているかを気にする必要はありません。EricLippertが書いているように、スタックは実装の詳細です。型がどのように渡されるか(値、参照など)のセマンティクスを理解することをお勧めします。

さらに、ヒープにオブジェクトを割り当てる方がスタックよりもコストがかかることを示唆しているようです。実際には、値型をコピーすることのパフォーマンスコストは、スタックへの割り当てがわずかに速くなるという節約のメリットを上回っていると私は主張します。スタックとヒープの最大の違いは、ほとんどのCPUアーキテクチャでは、スタックがCPUキャッシュに保持される可能性が高いため、キャッシュミスを回避できることです。

これは、懸念すべき最も重要な問題ではありません。タイプに値渡しのセマンティクスを含めるかどうかを決定する必要があります。そうでない場合は、おそらく参照型である必要があります。

于 2010-01-08T22:39:21.353 に答える
2

キーワードに騙されないでくださいnew。構造体ではオプションです。

C#には、ガベージコレクターとタイプセーフティを楽しんで、多くのメモリの詳細について心配する必要がない管理された世界があります。スタック/ヒープの違いは関係ありません。それはコピーセマンティクスに関するものです。

制御が必要なまれなケースでは、C#の安全でない(管理されていない)部分に実際のポインターなどがあります。

ただし、C#とC ++ではコストが異なるため、ゴーストをハントしないでください。管理されていない、寿命の短いオブジェクトは非常に安価です。そして、コンパイラーは最適化としてスタックに小さな配列を割り当てることができます。あなたはそれを知ることができず、どちらも気にする必要はありません。

于 2010-01-08T23:12:22.707 に答える
1

これは、C#で構造体とクラスを表示するための間違った方法です。C#では、構造体とクラスの違いは、割り当てられる場所ではなく、コピーのセマンティクスです。構造体には値のセマンティクスがあり、クラスには参照のセマンティクスがあります。C ++プログラマーは、値セマンティクスを持つスタック上のオブジェクトと参照セマンティクスを持つヒープ上のオブジェクトに慣れているため、これを詳しく読む傾向があります。

このメモリがどのように割り当てられるかは、ランタイムの実装の詳細です。ランタイムは、スタック、ヒープ、またはその他のハイブリッド割り当てスキームを使用できます。通常、構造体はスタックのようなものに割り当てられ、クラスはある種のヒープに割り当てられることは事実ですが、必須ではありません。たとえば、関数に割り当てられ、関数のスコープ外に渡されないクラスは、代わりにスタックに非常に簡単に割り当てることができます。

于 2010-01-08T23:04:09.027 に答える
1

それについて心配する必要はありません-あなたの頭はまだc/c ++の世界にあり、物事がどこに行くかが非常に重要になる可能性があります。CLRチームには、これを魔法のように速くすることを心配して一日中過ごす本当に賢い人がたくさんいます。

C#にはいくつかの落とし穴があり、メモリ使用量は通常、偶然にたくさんの小さなオブジェクトを作成することに関連しています(文字列=文字列+ループ内の他の文字列を実行するのは古典的です)

メモリ管理が原因でパフォーマンスの問題が発生していると本当に思っている場合に何が起こっているかを示すmemprofilerがあります

私はc#で多くのパフォーマンスの高いコード(グラフィックスレンダリングクライアント、ネットワークサーバー)を作成しましたが、これについて心配する必要はありませんでした

于 2010-01-08T22:48:47.713 に答える