DMD producing huge binaries

Georgi D via Digitalmars-d digitalmars-d at puremagic.com
Tue May 17 14:44:53 PDT 2016


Hi,

While working on a D project which heavily uses the lazy 
algorithms for ranges I noticed a sudden huge increase in the 
compilation time and the produced binary size.

I chased down the offending change to just a small change in one 
line of the code which made the resulting binary to jump from 
18MB to over 400MB.

The code that produces the large binary actually failed to link 
using DMD on osx (works on linux). The link command just consumes 
all the CPU and never completes. Using LDC on osx just failed to 
compile. ldc was using all the CPU and never completing.

There is also a very big difference between passing -unittest and 
not passing it.

I was able to shrink the code down to the following:

import std.range.primitives;
import std.range : chain, choose, repeat, chooseAmong, takeOne;
import std.algorithm.iteration : joiner , map;
import std.range.interfaces : InputRange, inputRangeObject;
import std.conv : text;

struct AType {
    string id;
    Annotation[] annotations;
    TypeArgument[] typeArguments;

}

struct Annotation {
    string type;
}

struct TypeArgument {
    AType refType;
}

struct PrimaryValue {
    int type;
}

struct FieldDecl {
    AType type;
    string id;
    PrimaryValue[] values;
}

struct MethodDecl {
    string name;
    AType returnType;
    FieldDecl[] params;
}

auto toCharsArray(R)(R r, string sep = "\n", string tail = "\n")
          if (isInputRange!R) {
     return choose(r.empty, "", chain(r.joiner(sep), tail));
}


auto toChars(Annotation uda) {
    return chain("@", uda.type);
}

InputRange!(ElementType!string) toChars(TypeArgument ta) {
    auto r = chain("", ta.refType.id);
    with (ta.refType) {
       auto tArgs = choose(typeArguments.empty, "",
                           chain("!(", 
typeArguments.map!(toChars).joiner(", "), ")"));

       return chain(id, tArgs).inputRangeObject();
    }
}

auto toChars(AType t) {
    auto udas = t.annotations.map!(toChars).toCharsArray();
    auto tArgs = choose(t.typeArguments.empty, "",
                        chain("!(", 
t.typeArguments.map!(toChars).joiner(", "), ")"));

    return chain(udas, t.id, tArgs);
}

/// PrimaryValue
auto toChars(PrimaryValue pv) {
    return chooseAmong(pv.type, "val", "val");
}

auto toCharsSingleOrArray(R)(R r) if (isInputRange!R) {
    return choose(r.length == 1,
                  r.takeOne.map!(toChars).joiner(),
                  chain("[", r.map!(toChars).joiner(", "), "]")
                  );
}

auto toCharsBare(FieldDecl f) {
    auto def = chain(f.type.toChars, " ", f.id);

    return choose(f.values.empty,
                  def,
                  chain(def, " = ", 
toCharsSingleOrArray(f.values)));
}

auto toChars(FieldDecl f) {
    return chain("", f.toCharsBare());
}

auto toChars(MethodDecl m) {
    //Just changing the line bellow to have map!(toChars) reduces 
the binary size
    //to 18MB from 403MB
    auto prms = chain("(", 
m.params.map!(toCharsBare).toCharsArray(", ", ""), ")");
    return chain(" ", m.name, prms, ";");
}

unittest {
    AType t1 = {id : "Foo"};
    FieldDecl f1 = {type : t1, id : "f1"};

    MethodDecl m1 = {name : "m", returnType : t1 };
    assert(!m1.toChars().text().empty); // removing this line 
drops the size by half
}

void main() { }

I am compiling this with:

# dmd -g -debug -unittest


I have marked the line that needs to be changed for the code size 
to drop significantly. What is interesting is that the using 
toChars should be more complex than using toCharsBare since the 
former calls the latter but the binary size just explodes when 
using toCharsBare.

I apologize for the long code segment.


More information about the Digitalmars-d mailing list