没有垃圾收集器的D编程

40 garbage-collection d

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

我自己从一个问题中发现的简单测试表明GC非常慢.比直接C++做同样事情慢10多倍.(显然,测试并没有直接转换成现实世界,但是性能受到了极大的打击并且会降低现实世界发生的行为类似(快速分配许多小对象)

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

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

我的问题是:

  1. 用标准智能指针实现替换D'GC有多困难,以便仍然可以使用依赖GC的库.如果我完全删除GC,我将失去很多繁琐的工作,因为D已经有限制库与C++相比.
  2. GC.Disable是否仅暂时停止垃圾收集(阻止GC线程运行),GC.Enable从中断处继续收回.所以我可能会禁用GC在高CPU使用时刻运行以防止延迟问题.
  3. 是否有任何方法可以强制执行模式以不一致地使用GC.(这是因为我没有在D编程,当我开始编写不使用GC的眼镜时,我想确保我不会忘记实施自己的清理.
  4. 是否可以轻松更换D中的GC?(不是我想要的,但有一天玩GC的不同方法可能会很有趣......这与我想的相似)

我想做的是交易记忆以提高速度.我不需要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.
}
Run Code Online (Sandbox Code Playgroud)

实际上,实际上能够将不同类型的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();
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

小智 19

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

  2. 来自D的图书馆参考.
    Core.memory:

    static nothrow void disable();
    
    Run Code Online (Sandbox Code Playgroud)

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

    static pure nothrow void free(void* p);
    
    Run Code Online (Sandbox Code Playgroud)

    释放p引用的内存.如果p为null,则不执行任何操作.如果p引用的内存最初并非由此垃圾收集器分配,或者它指向内存块的内部,则不会执行任何操作.无论是否设置了FINALIZE属性,都不会最终确定该块.如果需要最终确定,请改用delete.

    static pure nothrow void* malloc(size_t sz, uint ba = 0);
    
    Run Code Online (Sandbox Code Playgroud)

    从垃圾收集器请求对齐的托管内存块.可以通过调用free来随意删除该内存,也可以在收集运行期间自动将其丢弃和清除.如果分配失败,此函数将调用onOutOfMemory,该函数应该抛出OutOfMemoryError.

    是的 在这里阅读更多:http://dlang.org/garbage.html

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

    如果你真的需要课程,请看看:http://dlang.org/memory.html#newdelete 删除已被弃用,但我相信你仍然可以免费()它.

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

  4. 或多或少......在这里填写函数定义:https://github.com/D-Programming-Language/druntime/blob/master/src/gcstub/gc.d.设置了GC代理系统,允许您自定义GC.所以它不像是设计师不希望你做的事情.

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

import std.c.stdlib; 应该有malloc和free.

import 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.
Run Code Online (Sandbox Code Playgroud)

为什么要为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
Run Code Online (Sandbox Code Playgroud)

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

  • 从个人经验来看 - 如果人们只考虑编写GC友好代码,90%的性能抱怨都会消失...... (5认同)

Dej*_*kic 9

我建议你阅读这篇文章:http://3d.benjamin-thaut.de/?p = 20 在那里你会发现一个标准库的版本,它拥有内存管理并完全避免垃圾收集.


Tra*_*s3r 5

D'GC并不像Java那样复杂.它是开源的,所以任何人都可以尝试改进它.

有一个名为CDGC的实验性并发GC,目前有一个GSoC项目可以删除全局锁定:http://www.google-melange.com/gsoc/project/google/gsoc2012/avtuunainen/17001

确保使用LDC或GDC进行编译以获得更好的优化代码.

XomB项目也使用自定义运行时,但我认为它是D版本1. http://wiki.xomb.org/index.php?title=Main_Page