Skip to content
VLSI Mentor

Perl Part II · Module 16 · Perl in Semiconductor Engineering

Perl vs Python: The Honest Boundary

This question usually gets answered with a preference dressed up as a principle, and the result is that nobody trusts the answer. So this chapter does it differently: the same two jobs, written twice, measured on this machine, with both scripts shipped. One result favours Perl by a wide margin and the other favours Python — and that disagreement is not a problem with the test, it is the actual boundary. Once you can see which shape of task falls on which side, the decision for a given piece of work stops being a matter of taste and becomes something you can defend in a review, including to the colleague who disagrees.

Foundation13 min readPerlPart IIEDA Flows

Part II · Module 16 · Chapter 16.4 · Perl vs Python: The Honest Boundary

1. The Decision

Figure 16.4.1 — Which language a task belongs in

Three questions decide the language: whether the work is inside existing Perl, whether it is a throwaway one-liner, or whether it is new code that will be maintained.a task to automateask these in orderinside Perl already?stay in Perla throwaway?Perl: 55 chars, not 121new and lasting?Python: 11 lines, not 1412
Three questions, asked in this order, settle almost every case. If the work lives inside existing Perl, it stays Perl, because the cost of a rewrite is the risk measured in Chapter 16.2. If it is a one-off you will run at the prompt and never save, Perl wins on typing — measured at 55 characters against 121. Everything else is new code with a future, and that belongs in Python, which this chapter measures winning the structured case outright.

The first question is not about the languages at all. If the change belongs inside a working Perl script, it stays in Perl. Chapter 16.2 measured why: a 24-line driver with five environment variables is not a rewrite you take on casually, and "while I was in here I converted it" is the worst possible reason to.

The other two questions are the ones this chapter measures.

2. Where Perl Measurably Wins

Azvya Education Pvt. Ltd.VLSI Mentor
stream-filter.pl
use strict;
use warnings;
use Time::HiRes qw(time);
use File::Temp qw(tempdir);
use File::Spec;

my $dir = tempdir(CLEANUP => 1);
open my $w, ">", File::Spec->catfile($dir, "slack.txt") or die "cannot write: $!\n";
print $w "U1/Q 0.44 setup\nU2/D -0.03 hold\nU3/CK 0.12 setup\nU4/X -0.21 hold\n";
close $w;

# the job: print the pin and slack of every negative-slack row
my $pl = q{print "$F[0] $F[1]" if $F[1] < 0};
my $py = "import sys\nfor l in open('slack.txt'):\n"
       . "    f=l.split()\n"
       . "    if len(f)>=2 and float(f[1])<0: print(f[0],f[1])\n";

open my $pw, ">", File::Spec->catfile($dir, "j.py") or die "cannot write: $!\n";
print $pw $py;
close $pw;

my $a = `cd '$dir' && perl -lane '$pl' slack.txt`;
my $b = `cd '$dir' && python3 j.py`;
for ($a, $b) { s/\s+\z//; s/\n/ | /g }

print "the stream filter -- same answer from both:\n";
print "   perl  : $a\n";
print "   python: $b\n";
printf "   typed : perl %d chars, python %d chars\n\n",
    length(qq{perl -lane '$pl' slack.txt}), length($py) + length(q{python3 -c ""});

# startup, bucketed: a millisecond figure is not reproducible
my %cmd = (perl => [$^X, "-e", "1"], python3 => ["python3", "-c", "pass"]);
my (@ratio, $won);
for (1 .. 3) {
    my %ms;
    for my $lang (sort keys %cmd) {
        my $t0 = time;
        for (1 .. 60) { system(@{ $cmd{$lang} }) == 0 or die "spawn failed\n" }
        $ms{$lang} = (time - $t0) / 60;
    }
    push @ratio, $ms{python3} / $ms{perl};
    $won++ if $ms{perl} < $ms{python3};
}
my $mean = 0; $mean += $_ for @ratio; $mean /= @ratio;
my $band = $mean < 2 ? "under 2x" : $mean < 3 ? "2x to 3x"
         : $mean < 5 ? "3x to 5x" : $mean < 8 ? "5x to 8x" : "over 8x";
print "interpreter startup, 60 spawns x 3 rounds:\n";
printf "   perl started faster in %d of 3 rounds, ratio %s\n", $won, $band;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
the stream filter -- same answer from both:
   perl  : U2/D -0.03 | U4/X -0.21
   python: U2/D -0.03 | U4/X -0.21
   typed : perl 55 chars, python 121 chars

interpreter startup, 60 spawns x 3 rounds:
   perl started faster in 3 of 3 rounds, ratio 3x to 5x

Two measured wins, both narrow in scope and real.

Typing: 55 characters against 121. Not because Perl is terser in general, but because -lane supplies the open, the loop, the chomp and the split — the exact four things this job consists of. Chapter 15.8 measured those flags; this is the task shape they were designed for.

Startup: faster in 3 of 3 rounds, by a factor between 3 and 5. The absolute milliseconds are not published because they are not reproducible between runs; the band is. This matters in exactly one situation: a script invoked once per test by a regression. At ten thousand tests the difference is minutes of wall-clock spent starting interpreters and doing nothing else.

So Perl's win is on stream work at the command line, and per-invocation cost at scale. That is a narrower claim than "Perl is better at text", and it is the claim the measurement supports.

3. Where Python Measurably Wins

Now the same exercise on a task with structure, and the result goes the other way.

Azvya Education Pvt. Ltd.VLSI Mentor
structured-task.pl
use strict;
use warnings;
use File::Temp qw(tempdir);
use File::Spec;

my $dir = tempdir(CLEANUP => 1);
open my $w, ">", File::Spec->catfile($dir, "sta.rpt") or die "cannot write: $!\n";
print $w "clk_core U1/Q  0.44 setup\nclk_core U2/D -0.03 hold\n"
       . "clk_ddr  U3/CK 0.12 setup\nclk_ddr  U4/X -0.21 hold\n"
       . "clk_core U5/Y -0.07 setup\n";
close $w;

# the job: worst slack per clock domain, sorted by domain
my %impl = (
"worst.pl" => <<'PERL',
use strict;
use warnings;
my %worst;
open my $fh, "<", "sta.rpt" or die "cannot read: $!\n";
while (my $l = <$fh>) {
    my ($clk, $pin, $slack) = split ' ', $l;
    next unless defined $slack;
    $worst{$clk} = [$pin, $slack]
        if !exists $worst{$clk} || $slack < $worst{$clk}[1];
}
close $fh;
for my $clk (sort keys %worst) {
    printf "%-9s %-6s %6.2f\n", $clk, @{$worst{$clk}};
}
PERL
"worst.py" => <<'PY',
worst = {}
with open("sta.rpt") as fh:
    for l in fh:
        f = l.split()
        if len(f) < 3: continue
        clk, pin, slack = f[0], f[1], float(f[2])
        if clk not in worst or slack < worst[clk][1]:
            worst[clk] = (pin, slack)
for clk in sorted(worst):
    pin, slack = worst[clk]
    print(f"{clk:<9} {pin:<6} {slack:6.2f}")
PY
);
for my $n (sort keys %impl) {
    open my $f, ">", File::Spec->catfile($dir, $n) or die "cannot write: $!\n";
    print $f $impl{$n};
    close $f;
}

my $pl_out = `cd '$dir' && perl worst.pl`;
my $py_out = `cd '$dir' && python3 worst.py`;
print "output, perl:\n";   print "   $_\n" for split /\n/, $pl_out;
print "output, python:\n"; print "   $_\n" for split /\n/, $py_out;
printf "\nidentical output: %s\n\n", ($pl_out eq $py_out ? "yes" : "NO");

for my $n (sort keys %impl) {
    my @l = grep { /\S/ } split /\n/, $impl{$n};
    printf "   %-9s %2d non-blank lines, %3d chars\n", $n, scalar @l, length $impl{$n};
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
output, perl:
   clk_core  U5/Y    -0.07
   clk_ddr   U4/X    -0.21
output, python:
   clk_core  U5/Y    -0.07
   clk_ddr   U4/X    -0.21

identical output: yes

   worst.pl  14 non-blank lines, 392 chars
   worst.py  11 non-blank lines, 351 chars

Identical output, and Python is shorter: 11 non-blank lines and 351 characters against Perl's 14 and 392.

The reason is visible in the two sources. Perl stores the pair as an array reference, so getting it back out needs @{$worst{$clk}}, and deciding whether a key is new needs an explicit exists. Python's tuple unpack and in do both directly. Neither language is doing anything clever; Python's native structures simply fit this shape with less punctuation.

That is a real result and it is worth stating plainly: a comparison that favoured Perl in every test would be advocacy, not evidence. The two tests disagree, and the disagreement is informative:

  • stream shape — read lines, filter, print — Perl, decisively
  • structured shape — accumulate into a dictionary, group, sort, format — Python, modestly

Most work that is worth writing a file for is the second shape. Most work you do at a prompt and discard is the first.

4. What the Measurements Do Not Settle

Four factors decide more real cases than any number above, and none of them is a property of either language.

Who maintains it after you. A script in the language the team reads gets fixed. A script in the other language waits for you to come back from holiday. This usually outweighs everything else, and on most teams today it points at Python.

What the surrounding code is. A new step inside a Perl regression system is a Perl step, even if you would have chosen Python on a blank page. Mixing languages inside one flow stage adds a process boundary, a second set of error conventions and a second place to look when it breaks.

What the tool vendors give you. Some EDA tools embed a Tcl interpreter; some expose a Python API; some only read and write files. Where a tool has an API in one language, that language wins for anything talking to that tool, regardless of this chapter.

How long it will live. The honest half of this is that throwaway code lives forever. A one-liner that produces a number somebody likes becomes a script, then a Makefile rule, then part of signoff. If there is any chance of that, write it as a file with the Chapter 16.10 contract, in the language your team maintains.

5. The Rule

Putting the measurements and the judgement together:

Stay in Perl when the change belongs inside existing Perl, or the task is a true one-off at the prompt, or the script is invoked per-test in a large regression and startup cost is measurable.

Choose Python when the work is new, will be maintained, has structure rather than being a line filter, needs tests, or leads into data handling the standard library covers.

Learn to read Perl regardless. Nothing in this chapter suggests writing new Perl is the normal case. It suggests the opposite — and Chapter 16.9 is about why that makes reading it more valuable rather than less: the footprint you are not rewriting is the footprint you have to understand.

One closing note on how to use this. The questions in the figure are in that order deliberately, and the first one is about the code rather than the languages. Most arguments about Perl versus Python are really arguments about whether something counts as new work, and that is a much easier question to settle.

6. What To Remember

  • Ask three questions in order: is it inside existing Perl, is it a throwaway, is it new and lasting?
  • The first question is about the code, not the languages — and it settles most cases.
  • Never convert a script "while you are in there." Chapter 16.2 measured that cost.
  • Measured: the stream filter is 55 characters in Perl and 121 in Python — -lane supplies the four things that job consists of.
  • Measured: Perl starts faster in 3 of 3 rounds, by 3x to 5x. The band is published; the milliseconds are not reproducible.
  • Startup only matters per-invocation at scale — ten thousand tests makes it minutes.
  • Measured: the structured task is 11 lines in Python and 14 in Perl, with identical output. Python wins this one.
  • Perl needs @{$worst{$clk}} and an explicit exists where Python unpacks a tuple and uses in.
  • A comparison that favours one side in every test is advocacy. These two disagree, and that is the boundary.
  • Stream shape favours Perl; structured shape favours Python. Files are usually the second shape.
  • Testing and the next data step widen Python's margin, not narrow it.
  • Four things the numbers do not settle: who maintains it, what surrounds it, what the tool vendor supports, and how long it will really live.
  • Throwaway code lives forever — if there is any chance of that, write it properly in the team's language.
  • Learn to read Perl regardless. The footprint you are not rewriting is the one you must understand.

Chapter 16.5 puts the footprint on a map. With the boundary settled, the next question is which jobs in an RTL-to-signoff flow Perl actually owns — so that every module in the rest of Part II has a known place to sit.

Where this fits

Part of the Perl curriculum.