my first D program (and benchmark against perl)
Rikki Cattermole via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Wed Nov 11 06:20:47 PST 2015
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));
}
More information about the Digitalmars-d-learn
mailing list