没有垃圾收集器的D编程 [英] D programming without the garbage collector

查看:168
本文介绍了没有垃圾收集器的D编程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今天我一直在看D,表面上看起来相当惊人。我喜欢它如何在语言中直接包含许多更高级别的构造,所以不必使用愚蠢的黑客或简洁的方法。有一件事让我很担心,如果GC。我知道这是一个很大的问题,并且已经阅读了许多关于它的讨论。



我从这里的一个问题发现的简单测试表明,GC非常慢。比直接C ++做同样的事情要慢10倍以上。 (显然,测试并不直接转化为现实世界,但是性能打击是极端的,并且会减缓真实世界的行为发生类似行为(快速分配许多小对象)。

I我们正在研究编写一个实时低延迟音频应用程序,GC有可能会破坏应用程序的性能,使其几乎无用。从某种意义上说,如果它有任何问题,它将破坏实时音频方面是非常重要的,因为与图形不同,音频以更高的帧速率运行(44000+比30-60)(由于它的低延迟比标准的音频播放器可以缓冲大量的数据更重要) / p>

禁用GC可将结果提高到C ++代码的20%左右,这非常重要,我将在最后给出代码进行分析。



我的问题是:


  1. 用标准的智能指针代替D's GC有多困难履行这样仍然可以使用依赖于GC的库。如果我完全删除GC,我会失去很多烦人的工作,因为与C ++相比,D已经有了限制库。

  2. GC.Disable是否暂时暂停垃圾回收(阻止GC线程从运行)和GC.Enable选择它从哪里离开。所以我可能会禁用GC在CPU使用率高的时候运行,以防止延迟问题。

  3. 有没有什么办法强制一个模式不能一致地使用GC。 (这是因为我没有用D语言编程,所以当我开始写我的不使用GC的眼镜时,我想确保我不会忘记自己清理。

  4. 是否可以轻松替换D中的GC?(不是我想要的,但有一天玩了不同的GC方法可能很有趣......这与我想的类似)

我想要做的是交换内存以提高速度,我不需要GC每隔几秒就运行一次,事实上,如果我可以为我的数据结构正确实现我自己的内存管理,那么根本不需要经常运行,我可能需要在内存稀少的时候运行它,但从我读过的内容来看,因为在我的应用程序中通常会有一些时间我可以逃脱地调用它而没有问题,这将有助于缓解一些压力(但是再次,可能会有几个小时我不会能够称之为) 。



我并不担心内存限制。我宁愿在速度上浪费内存(当然,最多也是一点)。首先是延迟问题。



从我读过的内容来看,至少我可以走C / C ++的路线,只要我不不使用依赖GC的任何库或语言结构。问题是,我不知道那些做的。我已经看过字符串,新的等提到,但这是否意味着我不能使用字符串的构建,如果我不启用GC?



我已经请阅读一些错误报告,指出GC可能确实是错误的,这可以解释其性能问题?

另外,D使用更多内存,事实上,D在C ++程序之前耗尽内存。在这种情况下,我估计大约有15%左右。我认为这是针对GC的。



我知道以下代码并不代表您的平均程序,但它表示当程序实例化大量对象时比如在启动时),它们会慢得多(10倍是一个很大的因素)。如果我可以以某种方式让编译器自动进行GC分析,那么真正好的是什么呢?一个本地对象,如果我没有具体释放它。这几乎是两全其美的。



例如,

  {
Foo f = new Foo();
....
dispose f; //导致f被立即处理,并在GC
之外处理f //如果省略,则f传递给GC。
//我想这可能最终会在幕后创建两种Foo
//。

Foo g = new manualGC!Foo(); //也许这样的事情会使GC的手关闭
// g并允许手动处理它。
}

实际上,它可能很好,能够将不同类型的GC具有不同类型的数据,每个GC都是完全独立的。这样我就可以定制GC的性能。我的类型。

代码:

code> module main;
导入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开始,结束;
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(ⅰ);
}

end = clock();
cpu_time_used =(结束 - 开始);
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>

使用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开始,结束;
double cpu_time_used;




start = clock();

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


end = clock();
cpu_time_used =(结束 - 开始);
cpu_time_used = cpu_time_used / 1000.0;
cout<< cpu_time_used;

std :: cin.get();
返回0;


解决方案


  1. D可以使用几乎任何C库,只需定义所需的功能。 D也可以使用C ++库,但D不理解某些C ++结构。所以... D em可以使用几乎和C ++一样多的库。它们不是原生的D库。

  2. 来自D's Library参考。

    Core.memory:


      static nothrow void disable(); 

    禁用执行自动垃圾回收以最小化进程占用。在实现认为正确的程序行为所必需的情况下,例如在内存不足的情况下,集合可能会继续发生。该函数是可重入的,但每次调用禁用时都必须调用一次。

      static pure nothrow void free(void * p); 

    取消分配由p引用的内存。如果p为null,则不会发生任何操作。如果p引用的内存不是最初由这个垃圾收集器分配的内存,或者如果它指向内存块的内部,则不会采取任何操作。无论是否设置了FINALIZE属性,该块都不会完成。如果需要最终化,请使用delete。

     静态纯态nothrow void * malloc(size_t sz,uint ba = 0); 

    从垃圾收集器请求一个对齐的管理内存块。这个内存可以随意删除,免费,或者在收集运行期间自动清除并自动清理。如果分配失败,这个函数会调用onOutOfMemory,这会引发OutOfMemoryError。

    所以是的。阅读更多信息: http://dlang.org/garbage.html



    在这里: http://dlang.org/memory.html

    如果你确实需要类,请看这个: http://dlang.org/memory.html#newdelete
    delete已被弃用,但我相信您仍然可以免费()。


  3. 不要使用类,使用结构。结构是堆栈分配的,类是堆。除非你需要多态或类支持的其他东西,否则它们是你正在做的事情的开销。如果你愿意,你可以使用malloc和free。


  4. 或多或少......填写函数定义: https://github.com/D-Programming-Language/druntime/blob/master/src/gcstub/gc.d 。有一个GC代理系统可以让你自定义GC。因此,它不像是设计师不希望你做的事情。

  5. b
    $ b

    b $ b垃圾收集器不保证为所有未引用的对象运行析构函数。此外,未指定垃圾收集器为非引用对象调用析构函数的顺序。这意味着当垃圾收集器为具有引用垃圾回收对象的成员的类的对象调用析构函数时,这些引用可能不再有效。这意味着析构函数不能引用子对象。此规则不适用于使用DeleteExpression删除的自动对象或对象,因为析构函数未由垃圾回收器运行,这意味着所有引用都是有效的。

    导入std.c.stdlib;应该有malloc和free。



    import core.memory;这有GC.malloc,GC.free,GC.addroots,//将外部内存添加到GC ...

    字符串需要GC,因为它们是不可变的动态数组字符。 (不可变(char)[])动态数组需要GC,静态不需要。



    如果需要手动管理,请继续。

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

    char * one = cast(char *)GC.malloc(char.sizeof * 8);.
    GC.free(one); //对不起,我不习惯手动内存管理。
    //我*要求*您编辑这个来修复它,如果它需要的话。

    为什么要为int创建一个包装类?你只是在放慢速度,浪费内存而做的事。

      class Foo {int n; this(int _n){n = _n; }} 
    writeln(Foo.sizeof); //它是8字节,btw
    writeln(int.sizeof); //它的*一半* Foo的大小; 4字节。


    Foo [] m; // = new Foo [n]; // 8秒
    m.length = n; // 7秒轻微优化。至少在我的机器上。
    foreach(i; 0..n)
    m [i] = new Foo(i);


    int [] m;
    m.length = n; //很好的格式。并且默认初始化为0
    //哎呀!忘记了这个...
    foreach(i; 0..n)
    m [i] = i; // 145 sec

    如果你真的需要,那么在C中写入时间敏感函数,并从D中调用它。
    如果时间真的很重要,使用D的内联汇编来优化所有内容。


    I've been looking at D today and on the surface it looks quite amazing. I like how it includes many higher level constructs directly in the language so silly hacks or terse methods don't have to be used. One thing that really worries me if the GC. I know this is a big issues and have read many discussions about it.

    My own simple tests sprouted from a question here shows that the GC is extremely slow. Over 10 times slower than straight C++ doing the same thing. (obviously the test does not directly convert into real world but the performance hit is extreme and would slow down real world happens that behave similarly(allocating many small objects quickly)

    I'm looking into writing a real time low latency audio application and it is possible that the GC will ruin the performance of the application to make it nearly useless. In a sense, if it has any issues it will ruin the real time audio aspect which is much more crucial since, unlike graphics, audio runs at a much higher frame rate(44000+ vs 30-60). (due to it's low latency it is more crucial than a standard audio player which can buffer significant amounts of data)

    Disabling the GC improved the results to within about 20% of the C++ code. This is significant. I'll give the code at the end for analysis.

    My questions are:

    1. How difficult is it to replace D's GC with a standard smart pointers implementation so that libraries that rely on the GC can still be used. If I remove GC completely I'll lose a lot of grunt work, as D already has limit libraries compared to C++.
    2. Does GC.Disable only halt the garbage collection temporarily(preventing the GC thread from running) and GC.Enable pick back up where it left off. So I could potentially disable the GC from running in high cpu usage moments to prevent latency issues.
    3. Is there any way to enforce a pattern to not use GC consistently. (this is because I've not programming in D and when I start writing my glasses that do not use the GC I would like to be sure I don't forget to implement their own clean up.
    4. Is it possible to replace the GC in D easily? (not that I want to but it might be fun to play around with different methods of GC one day... this is similar to 1 I suppose)

    What I'd like to do is trade memory for speed. I do not need the GC to run every few seconds. In fact, if I can properly implement my own memory management for my data structures then chances are it will not need to run very often at all. I might need to run it only when memory becomes scarce. From what I've read, though, the longer you wait to call it the slower it will be. Since there generally will be times in my application where I can get away with calling it without issues this will help alleviate some of the pressure(but then again, there might be hours when I won't be able to call it).

    I am not worried about memory constraints as much. I'd prefer to "waste" memory over speed(up to a point, of course). First and foremost is the latency issues.

    From what I've read, I can, at the very least, go the route of C/C++ as long as I don't use any libraries or language constructs that rely on the GC. The problem is, I do not know the ones that do. I've seen string, new, etc mentioned but does that mean I can't use the build in strings if I don't enable the GC?

    I've read in some bug reports that the GC might be really buggy and that could explain its performance problems?

    Also, D uses a bit more memory, in fact, D runs out of memory before the C++ program. I guess it is about 15% more or so in this case. I suppose that is for the GC.

    I realize the following code is not representative of your average program but what it says is that when programs are instantiating a lot of objects(say, at startup) they will be much slower(10 times is a large factor). Of the GC could be "paused" at startup then it wouldn't necessarily be an issue.

    What would really be nice is if I could somehow have the compiler automatically GC a local object if I do not specifically deallocate it. This almost give the best of both worlds.

    e.g.,

    {
        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.
    }
    

    In fact, it might be nice to actually be able to associate different types of GC's with different types of data with each GC being completely self contained. This way I could tailor the performance of the GC to my types.

    Code:

    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++ code

    #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;
    }
    

    解决方案

    1. D can use pretty much any C library, just define the functions needed. D can also use C++ libraries, but D does not understand certain C++ constructs. So... D can use almost as many libraries as C++. They just aren't native D libs.

    2. From D's Library reference.
      Core.memory:

      static nothrow void disable();
      

      Disables automatic garbage collections performed to minimize the process footprint. Collections may continue to occur in instances where the implementation deems necessary for correct program behavior, such as during an out of memory condition. This function is reentrant, but enable must be called once for each call to disable.

      static pure nothrow void free(void* p);
      

      Deallocates the memory referenced by p. If p is null, no action occurs. If p references memory not originally allocated by this garbage collector, or if it points to the interior of a memory block, no action will be taken. The block will not be finalized regardless of whether the FINALIZE attribute is set. If finalization is desired, use delete instead.

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

      Requests an aligned block of managed memory from the garbage collector. This memory may be deleted at will with a call to free, or it may be discarded and cleaned up automatically during a collection run. If allocation fails, this function will call onOutOfMemory which is expected to throw an OutOfMemoryError.

      So yes. Read more here: http://dlang.org/garbage.html

      And here: http://dlang.org/memory.html

      If you really need classes, look at this: http://dlang.org/memory.html#newdelete delete has been deprecated, but I believe you can still free() it.

    3. Don't use classes, use structs. Structs are stack allocated, classes are heap. Unless you need polymorphism or other things classes support, they are overhead for what you are doing. You can use malloc and free if you want to.

    4. More or less... fill out the function definitions here: https://github.com/D-Programming-Language/druntime/blob/master/src/gcstub/gc.d . There's a GC proxy system set up to allow you to customize the GC. So it's not like it is something that the designers do not want you to do.

    Little GC knowledge here: The garbage collector is not guaranteed to run the destructor for all unreferenced objects. Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid. This means that destructors cannot reference sub objects. This rule does not apply to auto objects or objects deleted with the DeleteExpression, as the destructor is not being run by the garbage collector, meaning all references are valid.

    import std.c.stdlib; that should have malloc and free.

    import core.memory; this has GC.malloc, GC.free, GC.addroots, //add external memory to GC...

    strings require the GC because they are dynamic arrays of immutable chars. ( immutable(char)[] ) Dynamic arrays require GC, static do not.

    If you want manual management, go ahead.

    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.
    

    why create a wrapper class for an int? you are doing nothing more than slowing things down and wasting memory.

    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
    

    If you really need to, then write the Time-sensitive function in C, and call it from D. Heck, if time is really that big of a deal, use D's inline assembly to optimize everything.

    这篇关于没有垃圾收集器的D编程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆