Archive

Archive for October 2nd, 2002

Is Perl the new COBOL?

October 2nd, 2002 1 comment

It seems to me that COBOL and other popular high-level procedural languages should be blamed for most of the current problems in programming. It is so easy to program in such languages that even novice programmers can write large programs – but the result is often poorly structured. With OO languages such as Smalltalk, programmers take pains to learn how to program, and the result is well-structured programs. Having programming approaches that can be used only by experts may be a good idea. Our past attitude of easy-going programming is a major cause of the present software crisis.

– Takaya Ishida, in Steve McConnell’s “The Best Influences on Software Engineers”, Software, 2000

Tags: ,

Scripting – Higher Level Languages For The 21st Century

October 2nd, 2002 No comments

There’s been a lot of debate on the Extreme Programming list recently about Tcl vs Java, mostly spurred by Dossy’s citation of the Jeff Hobbs quote: I’ve come to the realization that there are so many more Java jobs than Tcl jobs because it takes 10 Java programmers to do what I can do in Tcl. I’m only half-joking…

Interestingly I came across an article today in IEEE Computer, from Jan 1998, that compared the scripting languages (particularly Tcl) with the system languages (C, C++, Java et al). It covers most of the usual ground, but also includes an interesting chart of some applications that, for one reason or another, ended up being written in both types of languages:

Application Comparison Code Ratio Effort Ratio
Database Application C++: 2 months
Tcl: 1 day
60:1
Computer System Test and Installation C Test Application: 272,000 LOC, 120 mths
C FIS Application: 90,000 LOC, 60 mths
Tcl/Perl: 7,700 LOC, 8 months
47:1 22:1
Database Library C++: 2 months
Tcl: 1 day
60:1
Database Library C++: 2-3 months
Tcl: 1 week
8-12:1
Security Scanner C: 3000 LOC
Tcl: 300 LOC
10:1
Display Oil Well Production Curves C++: 3 months
Tcl: 2 weeks
6:1
Query Dispatcher C 12,000 LOC, 4-8 weeks
Tcl: 500 LOC, 1 week
2.5:1 4-8:1
Spreadsheet Tool C: 1460 LOC
Tcl: 380 LOC
4:1
Simulator and GUI Java 3,400 LOC, 3-4 weeks
Tcl: 1600 LOC, under 1 week
2:1 3-4:1

Some of the reduction in effort is no doubt due to the TCL version usually being written second, but for at least one of the applications this wasn’t true. In many of the cases the TCL version also provided more functionality.

Tags:

The Great Language Shootout

October 2nd, 2002 1 comment

I was talking to Marty again recently about his anti-Java stance, and we were trying to think of ways in which different languages could be rated.

This of course reminded me to go back and check out Doug Bagley’s Great Language Shootout, which compares multiple languages’ speed and memory usage for doing the same task.

I’d previously helped optimise some of the Perl solutions, and last night I noticed that there were a few new tests from when I last looked at it. In partiuclar the results for the Matrix Multiplication test seemed slightly strange: Perl was much further down the list that I’d have expected.

The meat of the code seemed to be the function mmult:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sub mmult {
    my ($rows, $cols, $m1, $m2) = @_;
    my @m3 = ();
    --$rows; --$cols;
    for my $i (0 .. $rows) {
        my @row = ();
        my $m1i = $m1->[$i];
        for my $j (0 .. $cols) {
            my $val = 0;
            for my $k (0 .. $cols) {
                $val += $m1i->[$k] * $m2->[$k]->[$j];
            }
            push(@row, $val);
        }
        push(@m3, \@row);
    }
    return(\@m3);
}

I played around for a while, and got about a 50% speedup with quite a nasty nested map approach:

1
2
3
4
5
6
7
8
9
10
11
12
13
sub mmult2 {
  my ($rows, $cols, $m1, $m2) = @_;
  --$rows; --$cols;
  my @m3 = ();
  for (0 .. $rows) {
    my $i = $_;
    push @m3, [ map {
      my $j = $_;
      sum map $m1->[$i]->[$_] * $m2->[$_]->[$j], 0..$cols;
    } 0 .. $cols ]
  }
  return \@m3;
}

I tried various approaches to turn the outer for() into a map as well, but my brain started hurting too much as it all got very messy.

And then I noticed that we were pushing to @m3 each time around a loop that counted from 0, and realised it would probably be much more efficient to just assign directly each time. So I replaced the push @m3, ... with $m3[$i] = ... , and performance shot up.

So I rolled back all the other changes I’d made, and just applied this straight through:

1
2
3
4
5
6
7
8
9
10
11
sub mmult3 {
  my ($rows, $cols, $m1, $m2) = @_;
  my $m3 = [];
  --$rows; --$cols;
  for my $i (0 .. $rows) {
    for my $j (0 .. $cols) {
      $m3->[$i][$j] += $m1->[$i][$_] * $m2->[$_][$j] for 0..$cols;
    }
  }
  return $m3;
}

I think this version is much neater, more idiomatic Perl, and also more understandable and maintainable than not just my optimised one, but the original as well. And it’s 3 times faster.

Optimising for speed doesn’t always mean trading off maintainability. Usually finding a better approach gets better results that micro-optimisations, and can end up producing an all-round better solution, not just a faster one.

Unfortunately Doug has stopped updating the Shootout pages, so perl will just have to languish 4 places lower on this test than it should be …

Tags: ,