41

私は今日Dを見てきました、そして表面上それはかなり驚くべきように見えます。言語に直接多くの高レベルの構造が含まれているので、ばかげたハックや簡潔なメソッドを使用する必要がないのが好きです。GCの場合に本当に心配することが1つあります。私はこれが大きな問題であることを知っており、それについて多くの議論を読んだことがあります。

ここでの質問から生まれた私自身の簡単なテストは、GCが非常に遅いことを示しています。同じことを行うストレートC++よりも10倍以上遅くなります。(明らかに、テストは実際の世界に直接変換されませんが、パフォーマンスの低下は極端であり、同様に動作する現実の世界が遅くなります(多くの小さなオブジェクトをすばやく割り当てます)

リアルタイムの低遅延オーディオアプリケーションの作成を検討していますが、GCによってアプリケーションのパフォーマンスが低下し、ほとんど役に立たなくなる可能性があります。ある意味で、問題があると、リアルタイムのオーディオの側面が台無しになります。これは、グラフィックスとは異なり、オーディオがはるかに高いフレームレート(44000+対30-60)で実行されるため、はるかに重要です。(レイテンシーが低いため、大量のデータをバッファリングできる標準のオーディオプレーヤーよりも重要です)

GCを無効にすると、結果がC ++コードの約20%以内に改善されました。これは重要です。分析のために最後にコードを示します。

私の質問は次のとおりです。

  1. Gに依存するライブラリを引き続き使用できるように、DのGCを標準のスマートポインタ実装に置き換えることはどれほど難しいか。GCを完全に削除すると、DにはC ++と比較してすでに制限ライブラリがあるため、多くのうんざりする作業が失われます。
  2. GC.Disableは、ガベージコレクションを一時的に停止するだけで(GCスレッドが実行されないようにします)、GC.Enableは中断したところから再開します。そのため、レイテンシの問題を防ぐために、CPU使用率の高い瞬間にGCが実行されないようにする可能性があります。
  3. GCを一貫して使用しないようにパターンを強制する方法はありますか?(これは、私がDでプログラミングしていないためです。また、GCを使用しないメガネを書き始めるときは、独自のクリーンアップを実装することを忘れないでください。
  4. DのGCを簡単に交換することは可能ですか?(私がやりたいことではありませんが、ある日、GCのさまざまな方法を試してみるのは楽しいかもしれません...これは私が思う1に似ています)

私がやりたいのは、メモリとスピードを交換することです。GCを数秒ごとに実行する必要はありません。実際、データ構造に対して独自のメモリ管理を適切に実装できれば、それほど頻繁に実行する必要がなくなる可能性があります。メモリが不足したときにのみ実行する必要があるかもしれません。しかし、私が読んだことから、あなたがそれを呼ぶのを待つ時間が長くなるほど、それは遅くなります。私のアプリケーションでは通常、問題なく呼び出すことができる場合があるため、これはプレッシャーの一部を軽減するのに役立ちます(ただし、電話をかけられない場合もあります)。

私はメモリの制約についてはそれほど心配していません。私は速度よりもメモリを「無駄にする」ことを好みます(もちろん、ある程度まで)。何よりもまず、遅延の問題です。

私が読んだことから、GCに依存するライブラリや言語構造を使用しない限り、少なくともC /C++のルートをたどることができます。問題は、私はそうするものを知らないということです。文字列、新規などについて言及しましたが、GCを有効にしないと、文字列のビルドを使用できないということですか?

いくつかのバグレポートを読んだところ、GCは本当にバグがあり、パフォーマンスの問題を説明できる可能性がありますか?

また、Dはもう少し多くのメモリを使用します。実際、DはC++プログラムの前にメモリを使い果たします。この場合は約15%多いと思います。それはGC用だと思います。

次のコードは平均的なプログラムを表していないことを理解していますが、プログラムが多くのオブジェクトをインスタンス化する場合(たとえば、起動時)、それらははるかに遅くなります(10倍が大きな要因です)。GCのうち、起動時に「一時停止」される可能性がある場合は、必ずしも問題になるとは限りません。

本当に素晴らしいのは、特にローカルオブジェクトの割り当てを解除しない場合に、コンパイラにローカルオブジェクトを自動的にGCさせることができればです。これはほとんど両方の長所をもたらします。

例えば、

{
    Foo f = new Foo();
    ....
    dispose f; // Causes f to be disposed of immediately and treats f outside the GC
               // If left out then f is passed to the GC.
               // I suppose this might actually end up creating two kinds of Foo 
               // behind the scenes. 

    Foo g = new manualGC!Foo();   // Maybe something like this will keep GC's hands off 
                                  // g and allow it to be manually disposed of.
}

実際、さまざまなタイプのGCをさまざまなタイプのデータに関連付けて、各GCを完全に自己完結させることができると便利な場合があります。このようにして、GCのパフォーマンスを自分のタイプに合わせて調整することができました。

コード:

module main;
import std.stdio, std.conv, core.memory;
import core.stdc.time;

class Foo{
    int x;
    this(int _x){x=_x;}
}

void main(string args[]) 
{

    clock_t start, end;
    double cpu_time_used;


    //GC.disable();
    start = clock();

    //int n = to!int(args[1]);
    int n = 10000000;
    Foo[] m = new Foo[n];

    foreach(i; 0..n)
    //for(int i = 0; i<n; i++)
    {
        m[i] = new Foo(i);
    }

    end = clock();
    cpu_time_used = (end - start);
    cpu_time_used = cpu_time_used / 1000.0;
    writeln(cpu_time_used);
    getchar();
}

C++コード

#include <cstdlib>
#include <iostream>
#include <time.h>
#include <math.h>
#include <stdio.h>

using namespace std;
class Foo{
public:
    int x;
    Foo(int _x);

};

Foo::Foo(int _x){
    x = _x;
}

int main(int argc, char** argv) {

    int n = 120000000;
    clock_t start, end;
    double cpu_time_used;




    start = clock();

    Foo** gx = new Foo*[n];
    for(int i=0;i<n;i++){
        gx[i] = new Foo(i);
    }


    end = clock();
    cpu_time_used = (end - start);
    cpu_time_used = cpu_time_used / 1000.0;
    cout << cpu_time_used;

    std::cin.get();
    return 0;
}
4

6 に答える 6

19
  1. D は、必要な関数を定義するだけで、ほぼすべての C ライブラリを使用できます。D も C++ ライブラリを使用できますが、D は特定の C++ 構造を認識しません。つまり... DはC++ とほぼ同じ数のライブラリを使用できます。それらはネイティブの D ライブラリではありません。

  2. Dのライブラリ参照から。
    Core.memory:

    static nothrow void disable();
    

    プロセスのフットプリントを最小限に抑えるために実行される自動ガベージ コレクションを無効にします。コレクションは、メモリ不足の状態など、プログラムの正しい動作のために実装が必要であると判断した場合に発生し続ける可能性があります。この関数は再入可能ですが、無効にする呼び出しごとに enable を 1 回呼び出す必要があります。

    static pure nothrow void free(void* p);
    

    p によって参照されるメモリの割り当てを解除します。p が null の場合、アクションは発生しません。p が、このガベージ コレクタによって割り当てられていないメモリを参照している場合、またはメモリ ブロックの内部を指している場合、アクションは実行されません。FINALIZE 属性が設定されているかどうかに関係なく、ブロックはファイナライズされません。ファイナライズが必要な場合は、代わりに delete を使用してください。

    static pure nothrow void* malloc(size_t sz, uint ba = 0);
    

    ガベージ コレクターからマネージド メモリの整列されたブロックを要求します。このメモリは、free を呼び出すことで自由に削除できます。また、コレクションの実行中に破棄して自動的にクリーンアップすることもできます。割り当てが失敗した場合、この関数は、OutOfMemoryError をスローすると予想される onOutOfMemory を呼び出します。

    あ、はい。詳細はこちら: http://dlang.org/garbage.html

    そしてここ:http://dlang.org/memory.html

    本当にクラスが必要な場合は、これを見てください: http://dlang.org/memory.html#newdelete delete は廃止されましたが、まだ free() できると思います。

  3. クラスを使用しないで、構造体を使用してください。構造体はスタックに割り当てられ、クラスはヒープです。ポリモーフィズムやクラスがサポートする他のものが必要でない限り、それらはあなたがしていることのオーバーヘッドです。必要に応じて、malloc と free を使用できます。

  4. 多かれ少なかれ...ここに関数定義を記入してください: https://github.com/D-Programming-Language/druntime/blob/master/src/gcstub/gc.d。GC をカスタマイズできるように設定された GC プロキシ システムがあります。ですから、デザイナーがあなたに望んでいないことではありません。

ここでの GC の知識はほとんどありません。ガベージ コレクターは、参照されていないすべてのオブジェクトに対してデストラクタを実行するとは限りません。さらに、ガベージ コレクタが非参照オブジェクトのデストラクタを呼び出す順序は指定されていません。これは、ガベージ コレクタが、ガベージ コレクションされたオブジェクトへの参照であるメンバーを持つクラスのオブジェクトのデストラクタを呼び出すと、それらの参照が無効になる可能性があることを意味します。これは、デストラクタがサブ オブジェクトを参照できないことを意味します。デストラクタがガベージ コレクタによって実行されていないため、このルールは auto オブジェクトまたは DeleteExpression で削除されたオブジェクトには適用されません。つまり、すべての参照が有効です。

std.c.stdlib をインポートします。malloc と free が必要です。

core.memory をインポートします。これには GC.malloc、GC.free、GC.addroots があり、//外部メモリを GC に追加します...

文字列は不変の文字の動的配列であるため、GC が必要です。( immutable(char)[] ) 動的配列には GC が必要ですが、静的配列には必要ありません。

手動で管理したい場合は、先に進んでください。

import std.c.stdlib;
import core.memory;

char* one = cast(char*) GC.malloc(char.sizeof * 8);.
GC.free(one);//pardon me, I'm not used to manual memory management. 
//I am *asking* you to edit this to fix it, if it needs it.

int のラッパー クラスを作成する理由 あなたは物事を遅くし、メモリを浪費しているだけです。

class Foo { int n; this(int _n){ n = _n; } }
writeln(Foo.sizeof);  //it's 8 bytes, btw
writeln(int.sizeof);  //Its *half* the size of Foo; 4 bytes.


Foo[] m;// = new Foo[n]; //8 sec
m.length=n; //7 sec minor optimization. at least on my machine.
foreach(i; 0..n)
    m[i] = new Foo(i);


int[] m;
m.length=n; //nice formatting. and default initialized to 0
//Ooops! forgot this...
foreach(i; 0..n)
    m[i] = i;//.145 sec

本当に必要な場合は、時間に依存する関数を C で記述し、D から呼び出します。時間が本当に重要な場合は、D のインライン アセンブリを使用してすべてを最適化します。

于 2012-11-27T00:41:31.777 に答える
9

この記事を読むことをお勧めします: http://3d.benjamin-thaut.de/?p=20 そこには、独自のメモリ管理を行い、ガベージ コレクションを完全に回避する標準ライブラリのバージョンがあります。

于 2012-11-27T00:38:53.967 に答える
5

D の GC は、Java のような他のものほど洗練されていません。オープンソースなので、誰でも改善を試みることができます。

CDGC という名前の実験的な同時実行 GC があり、グローバル ロックを削除する現在の GSoC プロジェクトがあります。

より最適化されたコードを取得するには、コンパイルに LDC または GDC を使用してください。

XomB プロジェクトもカスタム ランタイムを使用していますが、D バージョン 1 だと思います。 http://wiki.xomb.org/index.php?title=Main_Page

于 2012-11-27T11:23:41.190 に答える
4

必要なすべてのメモリブロックを割り当ててから、メモリプールを使用してGCなしでブロックを取得することもできます。

ちなみに、あなたが言ったほど遅くはありません。また、GC.disable()は実際には無効にしません。

于 2012-12-10T11:06:03.590 に答える
3

この問題を少し異なる視点から見ることができます。質問の理論的根拠として言及した、多くの小さなオブジェクトを割り当てることの最適ではないパフォーマンスは、GC だけとはほとんど関係がありません。むしろ、汎用 (ただし最適ではない) メモリ管理ツールと高性能 (ただしタスクに特化した) メモリ管理ツールの間のバランスの問題です。アイデアは次のとおりです。GC の存在によってリアルタイム アプリの作成が妨げられることはありません。特別な場合には、より具体的なツール (オブジェクト プールなど) を使用する必要があるだけです。

于 2012-12-05T21:44:09.093 に答える
2

これはまだクローズされていないため、最近のバージョンの D には std.container ライブラリがあり、組み込み配列よりもメモリに関してはるかに効率的な配列データ構造が含まれています。ライブラリ内の他のデータ構造も効率的かどうかは確認できませんが、ガベージ コレクションを必要としないデータ構造を手動で作成することなく、メモリをより意識する必要がある場合は、検討する価値があるかもしれません。

于 2013-03-07T16:19:34.487 に答える