On Sunday, 5 February 2017 at 04:22:30 UTC, rikki cattermole wrote:
On 05/02/2017 5:02 PM, thedeemon wrote:

snip

It may look so from a distance. But in my experience it's not that bad. In most software I did in D it did not matter really (it's either 64-bit or short lived programs) and the control D gives to choose how to deal with everything makes it all quite manageable, I can decide what to take
from both worlds and hence pick the best, not the worst.

The best of both worlds can be done quite simply.

Instead of a chain of input ranges like:

int[] data = input.filter!"a != 7".map!"a * 2".array;

Use:

int[] data;
data.length = input.length;

size_t i;
foreach(v; input.filter!"a != 7".map!"a * 2") {
        data[i] = v;
        i++;
}

data.length = i;

Of course this is dirt simple example, but instead look at it for e.g. a csv parser with some complex data structure creation + manipulation.

I have some real world code here[0] that uses it. Not only is there less allocations and uses the GC but also it ends up being significantly faster!

[0] https://gist.github.com/rikkimax/42c3dfa6500155c5e441cbb1437142ea#file-reports-d-L124

Some data to weigh that in order to compare different memory management strategies on that simple case:

#!/usr/bin/env rdmd

import std.conv;
import std.stdio;
import std.array;
import std.range;
import std.algorithm;

auto input = [1, 2, 7, 3, 7, 8, 8, 9, 7, 1, 0];


void naive() {
    int[] data = input.filter!(a => a!= 7).map!(a => a*2).array;
    assert(data == [2, 4, 6, 16, 16, 18, 2, 0], data.to!string);
}

void maxReallocs() {
    int[] data;

    size_t i;
    foreach(v ; input.filter!(a => a!=7).map!(a => a*2)) {
        data ~= v;
    }

    assert(data == [2, 4, 6, 16, 16, 18, 2, 0], data.to!string);
}

void betterOfTwoWorlds() {
    int[] data;
    data.length = input.length;

    size_t i;
    foreach(v ; input.filter!(a => a!=7).map!(a => a*2)) {
        data[i] = v;
        i++;
    }
    data.length = i;

    assert(data == [2, 4, 6, 16, 16, 18, 2, 0], data.to!string);
}

void explicitNew() {
    int[] data = new int[input.length];
    scope(exit) delete data;

    size_t i;
    foreach(v ; input.filter!(a => a!=7).map!(a => a*2)) {
        data[i] = v;
        i++;
    }
    data.length = i;

    assert(data == [2, 4, 6, 16, 16, 18, 2, 0], data.to!string);
}

void cStyle() @nogc {
    import std.c.stdlib;

    int* data = cast(int*)malloc(input.length * int.sizeof);
    scope(exit) free(data);

    size_t i;
    foreach(v ; input.filter!(a => a!=7).map!(a => a*2)) {
        data[i++] = v;
    }

debug assert(data[0..i] == [2, 4, 6, 16, 16, 18, 2, 0], data.to!string);
}

void onTheStack() @nogc {
    int[100] data;

    size_t i;
    foreach(v ; input.filter!(a => a!=7).map!(a => a*2)) {
        data[i++] = v;
    }

debug assert(data[0..i] == [2, 4, 6, 16, 16, 18, 2, 0], data.to!string);
}

void main(string[] args) {
    import std.datetime;
    benchmark!(
        naive,
        maxReallocs,
        betterOfTwoWorlds,
        explicitNew,
        cStyle,
        onTheStack
    )(100000).each!writeln;
}

/* Results:

Compiled with dmd -profile=gc test.d
====================================

TickDuration(385731143)  // naive,
TickDuration(575673615)  // maxReallocs,
TickDuration(255928562)  // betterOfTwoWorlds,
TickDuration(270497154)  // explicitNew,
TickDuration(97596363)   // cStyle,
TickDuration(96467459)   // onTheStack

GC usage:

bytes allocated, allocations, type, function, file:line
       17600000          100000 int[] test.explicitNew test.d:43
4400000 100000 int[] test.betterOfTwoWorlds test.d:30
        3200000          800000 int[] test.maxReallocs test.d:22
        3200000          100000 int[] test.maxReallocs test.d:25
        3200000          100000 int[] test.explicitNew test.d:51
        3200000          100000 int[] test.explicitNew test.d:53
3200000 100000 int[] test.betterOfTwoWorlds test.d:37 3200000 100000 int[] test.betterOfTwoWorlds test.d:39 3200000 100000 std.array.Appender!(int[]).Appender.Data std.array.Appender!(int[]).Appender.this /usr/include/dlang/dmd/std/array.d:2675
        3200000          100000 int[] test.naive test.d:14

Compiled with dmd -O -inline test.d
===================================

TickDuration(159383005)  // naive,
TickDuration(187192137)  // maxReallocs,
TickDuration(94094585)   // betterOfTwoWorlds,
TickDuration(102374657)  // explicitNew,
TickDuration(41801695)   // cStyle,
TickDuration(45613954)   // onTheStack

Compiled with dmd -O -inline -release -boundscheck=off test.d
=============================================================

TickDuration(152151439)  // naive,
TickDuration(140870515)  // maxReallocs,
TickDuration(46740440)   // betterOfTwoWorlds,
TickDuration(59089016)   // explicitNew,
TickDuration(26038060)   // cStyle,
TickDuration(25984371)   // onTheStack

*/

Reply via email to