my first D program (and benchmark against perl)

Rikki Cattermole via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Nov 11 18:37:33 PST 2015


On 12/11/15 3:20 AM, Rikki Cattermole wrote:
> 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));
> }

I didn't realize that leftJustify strips out whitespace. Just throw an 
assignment in the first foreach rownum loop. That strips out hint 
std.string : strip.

Although I would be interested in seeing the performance of this in e.g. 
ldc and gdc.


More information about the Digitalmars-d-learn mailing list