On 12/11/15 2:31 AM, perlancar wrote:
Here's my first non-hello-world D program, which is a direct translation
from the Perl version. I was trying to get a feel about D's performance:

---BEGIN asciitable.d---
import std.string;
import std.stdio;

string fmttable(ref string[][] table) {
     string res = "";

     // column widths
     int[] widths;

     if (table.length == 0) return "";

     widths.length = table[0].length;

     for (int colnum=0; colnum < table[0].length; colnum++) {
         int width = 0;
         for (int rownum=0; rownum < table.length; rownum++) {
             if (table[rownum][colnum].length > width)
                 width = cast(int) table[rownum][colnum].length;
         }
         widths[colnum] = width;
     }

     for (int rownum=0; rownum < table.length; rownum++) {
         res ~= "|";
         for (int colnum=0; colnum < table[rownum].length; colnum++) {
             res ~= leftJustify(table[rownum][colnum], widths[colnum]);
             res ~= "|";
         }
         res ~= "\n";
     }

     return res;
}

void main() {
     // tiny table (1x1)
     /*
     string[][] table = [
         ["row1.1"],
     ];
     */

     // small table (3x5)
     string[][] table = [
         ["row1.1", "row1.2  ", "row1.3"],
         ["row2.1", "row2.2", "row2.3"],
         ["row3.1", "row3.2", "row3.3  "],
         ["row4.1", "row4.2", "row4.3"],
         ["row5.1", "row5.2", "row5.3"],
     ];

     write(fmttable(table));
     for (int i=0; i < 1000000; i++) {
         fmttable(table);
     }
}
---END asciitable.d---

Perl version:

---BEGIN asciitable.pl---
#!/usr/bin/env perl

sub fmttable {
     my $table = shift;

     my $res = "";

     # column widths
     my @widths;

     if (@$table == 0) { return "" }

     for my $colnum (0 .. $#{$table->[0]}) {
         my $width = 0;
         for my $rownum (0 .. $#{$table}) {
             if (length($table->[$rownum][$colnum]) > $width) {
                 $width = length($table->[$rownum][$colnum]);
             }
         }
         $widths[$colnum] = $width;
     }

     for my $rownum (0..$#{$table}) {
         $res .= "|";
         for my $colnum (0..$#{$table->[$rownum]}) {
             $res .= sprintf("%-".$widths[$colnum]."s|",
$table->[$rownum][$colnum]);
         }
         $res .= "\n";
     }
     $res;
}

# tiny table (1x1)
#my $table = [["row1.1"]];

# small table (3x5)
my $table = [
     ["row1.1", "row1.2", "row1.3"],
     ["row2.1", "row2.2  ", "row2.3"],
     ["row3.1", "row3.2", "row3.3  "],
     ["row4.1", "row4.2", "row4.3"],
     ["row5.1", "row5.2", "row5.3"],
];

print fmttable($table);

for (1..1_000_000) {
     fmttable($table);
}
---END asciitable.pl---

While I am quite impressed with how easy I was able to write D, I am not
so impressed with the performance. Using rdmd (build 20151103), the D
program runs in 17.127s while the Perl version runs in 11.391s (so the D
version is quite a bit *slower* than Perl's). While using gdc (Debian
4.9.2-10), I am able to run it in 3.988s (only about 3x faster than
Perl's version).

I understand that string processing (concatenation, allocation) is quite
optimized in Perl, I was wondering if the D version could still be sped
up significantly?

I turned it into mostly using large allocations, instead of small ones.
Although I'd recommend using Appender instead of my custom functions for this.

Oh and for me, I got it at 2 secs, 513 ms, 397 μs, and 5 hnsecs. Unoptimized, using dmd. When release mode is enabled on dmd: 1 sec, 550 ms, 838 μs, and 9 hnsecs. So significant improvement even with dmds awful optimizer.

import std.string;
import std.stdio;

static string SPACES = " ";

string fmttable(string[][] table) {
    char[] res;

    // column widths
    int[] widths;
    size_t totalSize;

    if (table.length == 0) return "";

    widths.length = table[0].length;

    foreach(colnum; 0 .. table[0].length) {
        int width = 0;
        size_t count;

        foreach(rownum; 0 .. table.length) {
            if (table[rownum][colnum].length > width)
                width = cast(int) table[rownum][colnum].length;
            count += table[rownum].length;
        }

        totalSize += ((width + 1) * count) + 2;
        widths[colnum] = width;
    }

    char[] buffer = new char[](totalSize);

    void assignText(string toAdd) {
        if (res.length < buffer.length - toAdd.length) {
        } else {
            buffer.length += toAdd.length;
        }

        res = buffer[0 .. res.length + toAdd.length];
        res[$-toAdd.length .. $] = toAdd[];
    }


    foreach(rownum; 0 .. table.length) {
        assignText("|");
        foreach(colnum; 0 .. table[rownum].length) {
assignText(SPACES[0 .. widths[colnum] - table[rownum][colnum].length]);
            assignText(table[rownum][colnum]);
            assignText("|");
        }
        assignText("\n");
    }

    return cast(string)res;
}

void main() {
    // tiny table (1x1)
    /*
    string[][] table = [
        ["row1.1"],
    ];
    */

    // small table (3x5)
    string[][] table = [
        ["row1.1", "row1.2  ", "row1.3"],
        ["row2.1", "row2.2", "row2.3"],
        ["row3.1", "row3.2", "row3.3  "],
        ["row4.1", "row4.2", "row4.3"],
        ["row5.1", "row5.2", "row5.3"],
    ];

import std.datetime : StopWatch, TickDuration, Duration;
StopWatch sw;
TickDuration start = sw.peek();
sw.start();

    write(fmttable(table));
    for (int i=0; i < 1000000; i++) {
        fmttable(table);
    }
sw.stop();

    writeln(cast(Duration)(sw.peek() - start));
}

Reply via email to