Teaching Parrot to Say “PHP”

A Speculative Look Ahead to PHP 6 and the Parrot VM

This article was first published as the cover story in the issue of International PHP Magazine.

Several months ago, I held a conversation with a fellow programmer about the relevance and performance of PHP-­GTK. The fear, said he, is that PHP-­GTK is too slow to run robust desktop applications. “Yet, how can this fear be plausible when so many already use PHP for robust Web­-based applications?” I retorted in my infinite wisdom. I went on to exclaim, “What PHP needs is a Virtual Machine—something like the Java Runtime Environment that all end­-users can easily install to run PHP applications!” “That is why I am placing my bets on Parrot,” was the astute programmer’s reply.

Parrot? Any number of brightly colored, tropical birds, some of which can imitate human speech?

No. Parrot: “a virtual machine designed to execute bytecode for interpreted languages efficiently”.

Thus began a long journey in which this writer traveled far and wide across the Internet searching for references to Parrot and the rumoured project in which PHP is being formulated to run on Parrot.

What Parrot Is

Parrot is a register-based virtual machine (VM) that is being specifically built for Perl 6. However,the beauty of its design is that it is language-agnostic. It can run any interpreted, dynamic language for which a compiler to Parrot Intermediate Representation (PIR) exists. Furthermore, it is designed to be lightweight, compact, and fast. Since it is not designed for statically typed languages, it can allow a significant performance increase to all the interpreted languages it may one day support over .NET and the Java Virtual Machine (JVM).

Parrot is also the result of an elaborate April Fool’s joke executed by Simon Cozens in 2001 where Larry Wall (Perl creator) and Guido van Rossum (Python creator) announced the merger of Perl and Python, forming the language Parrot1. Oddly enough, the Parrot VM realizes this hoax in ways that could not have been imagined. The joke was on Cozens, in other words. According to the Parrot FAQ, “Simon spent time as Parrot’s lead developer” for penance, “but he’s gotten better”.

What Parrot Isn’t

Parrot isn’t Perl 6, and it isn’t a language in and of itself, though it does have two internal languages, Parrot Assembly (PASM) and PIR, for compiling down to Parrot byte-code.

Parrot isn’t a plan to oust all the popular interpreted languages. Rather, it is a vehicle that could potentially help each language grow into more widespread use since there will only be one installable VM to support a wide variety of languages.

Parrot isn’t yet production-ready. Version 0.1.0 was released earlier this year, but it’s still in the primordial stages of development, and many more features are being added to it.

Finally, it is no more an April Fool’s joke, but a reality.

Why Parrot?

The most obvious reason for Parrot is one-ness of environment. The intention is to port Parrot to various operating systems with various architectures. The gain is that the communities building the languages that will run on Parrot no longer needs to worry about the platform; they can focus solely on developing the language knowing that it will run on any platform that Parrot can handle. Furthermore, Parrot provides JIT, or dynamic translation, full Unicode support, and language interoperability. These features are enough to woo any dy- namic language to the Parrot flock.

The Multilingual Bird

As mentioned, Parrot can support a variety of dynamic languages. This article, of course, deals with one in particular: PHP. Yet, in addition to Perl 6, there are already projects to port Python, Ruby, and Tcl to Parrot. One can see the power of a VM that speaks multiple languages. It would naturally have the power of each language and the individual benefits that go along with using each one. The compilers for each language can be written in such a way to harness the abilities of any of the other languages that Parrot speaks. This is true interoperability. Imagine PHP with the ability to use CPAN modules and Perl 6’s regular expression libraries. All languages on Parrot will have this sort of leverage!

Teaching Parrot to Talk

The question comes up, Well, how do you make it talk? How do you port a language to Parrot?

Parrot comes with two built-in languages, PASM and PIR (also known as Intermediate Code, or IMC). Either of these languages may be used to write a program and run it through Parrot, which will then convert it to byte-code and either output the byte-code or execute it, depending on the options given. The Parrot Web site suggests IMC as the target output for language compilers.

The differences between PASM and IMC are out of this article’s scope, but suffice it to say that IMC is a friendlier language to write, and the IMC compiler in Parrot generally uses fewer registers than PASM.

Thus, when writing a compiler for running a language on Parrot, the general idea is that the compiler converts the high-level language, such as PHP, to a medium-level language, IMC. Then, Parrot converts the IMC to byte-code and executes it or saves it for later execution.

The Pint Project

That’s great and all, but what does this have to do with PHP?

A little over a year ago, talk began surfacing on the Web about a “PHP on Parrot” project rumoured to be in the works. Blogs tossed the idea back and forth, toying with the notion since similar other projects had begun with Ruby and Python on Parrot, but then Sterling Hughes and Thies Arntzen decided to make this a reality, and, in October 2003 at PHPCon West, they delivered the closing keynote address “PHP and Parrot”.

The keynote address outlined many of the key points and reasons for creating a PHP on Parrot compiler, stressing the fact that this project is merely a proof-of-concept “that may turn into something more”2.

A year later, Arntzen still maintains that Pint, the name by which the project has come to be known, is only a proof-of-concept—at this point. When asked whether Pint could be the engine for PHP 6, Arntzen says, “I think it’s way too early to start speculating about PHP 6. Today I see our ‘baby’ more in the lines of Jython (Python on Java) or IronPython (Python on .NET) which both offer an alternative implementation of Python without necessarily being—or having to do with—the ‘future of python.’”

Despite the downplay of Pint as the potential next-generation engine for PHP, there is a buzz of excitement about it, and some have already begun setting their sights on Pint. In a January 2004 interview with Robert Adkins, Rasmus Lerdorf, creator of PHP, confessed, “My personal view on what I’d like to try, though I have no idea if it will work, is to go for Parrot”3.

This raises many questions surrounding the future of the PHP language itself, but these questions may be immaterial. Lerdorf’s response to inquiries about Pint may say it all: “Ask me again in six months”. It is just too early to tell. However, this doesn’t keep would be speculators from playing with the toys and considering the options.

The Parrot Says “Hello”…and Grabs a Pint

So, what does Parrot code look like, and how does Pint fit into the picture?

The code for Parrot is easy enough to find. Just download it from parrotcode.org. The Pint code is much harder to come by, as it hasn’t yet been distributed to the masses. Thies Arntzen, however, was nice enough to divulge the code, which currently bears the following license agreement:

This work is the unpublished copyrighted work of Thies Arntzen and Sterling Hughes. It may not be copied, modified, or distributed without their express permission.

He assures me, though, that a “BSD-style [license] seems very likely” when he and Hughes are ready to release the code to the public. Until then, playing with Parrot is exciting enough.

Now, consider the following ‘hello, world’ example in PHP:

<?php
echo "hello, world!\n";
?>

The code is simple enough, and the intended output is easy to visualize. The idea behind Pint is to convert this code into IMC before running it through the Parrot VM.

Right now, this action is a two-step process:

  1. Run the code through Pint and save the output, and
  2. Run Pint’s output through Parrot It looks like this:
[ramsey@orome pint]$ pint --format imc hello.php > hello.imc
[ramsey@orome pint]$ parrot hello.imc
hello, world!

Although it is a two-step process, it may also be performed in one statement:

[ramsey@orome pint]$ pint --format imc hello.php > hello.imc | parrot hello.imc
hello, world!

Either way, the results are the same. Pint outputs the IMC version of hello.php, which is then saved to hello.imc and executed by parrot. As for the IMC, the code is as simple as its PHP counterpart:

.sub _main
    print "hello, world!\n"
    end
.end

The Parrot Cracks Nuts and Crunches Numbers

The question that is oft on one’s mind when looking at Parrot and Pint is: how does it stand up against PHP—that is, the Zend Engine—in a benchmark test?

Arntzen clarifies: “As the codegen is far from being perfect—or even good—and also ‘cause we’re not supporting the full language syntax—yet—any kinds of benchmarks would be unfair. I’d also like to say that today’s PHP is fast enough to solve the ‘Web problem,’ so speed is usually not an issue. We do believe that Parrot can run PHP as fast as PHP can run PHP in all cases”.

Though the compiler is incomplete at this stage (indeed, it is lacking such crucial elements as classes and objects, among other things), Arntzen’s last words here are an understatement. Not only does Pint, as it currently supports the PHP language, run PHP as fast as PHP, it runs it faster than PHP.

Arntzen and Hughes illustrated the speed of Pint/Parrot over PHP at the PHPCon West conference through the use of a Mandelbrot fractal. The result showed PHP processing the code in 2.4 or 1.2 (with hacked PHP) seconds, while Pint on Parrot processed the code in 0.5 or 0.08 (with JIT) seconds2.

Listing 1. mandel.php distributed with Pint
<?php
$b = " .:,;!/>)|&IH%*#";
//float r, i, z, Z, t, c, C;
for ($y=30; $C = $y*0.1 - 1.5, $y--;){
echo "\n";
for ($x=0; $c = $x*0.04 - 2, $z=0, $Z=0, $x++ < 75;){
for ($r=$c, $i=$C, $k=0; $t = $z*$z - $Z*$Z + $r, $Z = 2*$z*$Z + $i, $z=$t, $k<50; $k++)
if ($z*$z + $Z*$Z > 10) break;
echo $b[$k%16];
}
}
?>

Using the Mandelbrot fractal code distributed with Pint (see Listing 1), I ran a similar test on a 2.4 Ghz Pentium 4 machine with 512 MB of RAM running Fedora Core 2 Linux. While this test is far from scientific, it is a good preliminary indicator of the speed difference between Parrot and PHP. Still, to be somewhat methodical about it, I ran the test fifteen times each for PHP and Pint/Parrot using the *NIX time utility and the following commands:

time php mandel.php
time pint --format imc mandel.php > mandel.imc | parrot mandel.imc

Table 1 (see below) displays the results of the fifteen tests in elapsed real time (in seconds). Perhaps this is an unfair benchmark due to the current stage of the project, but it is clear from these numbers alone that Pint/Parrot can crunch numbers with the best of them—perhaps even better.

See Listing 2 for the complete Pint-generated IMC for the Mandelbrot fractal.

When Will the Parrot Talk?

While we have a few benchmarks and some examples of how Pint will work on Parrot, the fact still remains that both Parrot and Pint are moving targets. That is, they are subject to change. So, the question is: when will Pint/Parrot be available for general, or production, use?

The Parrot VM itself is already talking up a storm. From blogs all across the Web to the fully-functioning VM, the Parrot squawks loud and clear. However, Parrot is currently not ready for production, and neither is Pint, which is quite further from production-ready than is Parrot.

Parrot is initially being developed for Perl 6, and while the next major version of Perl is far away, the Parrot developers maintain that the syntax is fairly stable and unlikely to change, which means that Parrot can continue development without fear of Perl 6 breaking it.

“Right now development is not moving at all as Sterling [Hughes] and myself are tied up with paid work,” Arntzen explains. “Both of us would love to work on it, but both also have to eat and pay rent”.

Pint: The Future of PHP?

One possible direction is that Pint will become the future of PHP, though Arntzen is hesitant to make this statement. “I don’t think that we’ll see a fork in PHP anytime soon. If one day—and only if—Pint, the compiler, and Parrot, the VM, can run PHP as good as PHP can run PHP, it might become an alternative to what is known as PHP today. Everything else is not up to me to decide”. Still, the key to development on both Pint and Parrot is time.

In Adkins’s interview, Lerdorf presents a wholly different outlook: “[Pint] would basically replace [the Zend Engine]. Just like Zend replaced my engine a couple of years ago. We’d go for the best engine along the way”3.

There are other issues and questions to tackle with such a move, among them the porting of the Zend extensions, if they are ported at all, and any garbage cleanup that might take place to remove inconsistencies left in PHP to support legacy code. Yet these choices are ultimately up to the PHP Group, who oversees the direction of the PHP project.

Arntzen, who is a member of the PHP Group, posits three methods for handling the extensions:

  1. Rewrite them entirely for Parrot, using Parrot’s native-call interface
  2. Use an appropriate Perl 6 CPAN module “and write a thin layer—in PHP—on top so that it becomes API-compatible with today’s PHP (for example, use DBI to implement mysql_connect())”
  3. Write a ‘thin glue’ that allows Pint to use the existing Zend extensions

“I think that we’ll start with three and then go to two and in the end it will all become Perl 6 CPAN (one and two),” he says, explaining the possible route Pint might take. He goes on to say that his “current idea is to write a Zend extension that you can load into your (today’s) PHP that replaces the compiler and the executor whilst still using all of the PHP framework (SAPI, extensions, etc.). So to use Pint, all you would have to do is say zend_extension=/usr/local/lib/pint.so in your php.ini—voila!”

Closing Squawks

Parrot, while having been around for several years now, is an up-and-comer in the world of VMs. Pint is newer still. At this point, it’s difficult to say how the cards will be dealt. Yet, one thing we know for sure is that Parrot will be the new vehicle for Perl 6. Beyond that, no one can make any statements with certainty.

Still, without a doubt, PHP has an exciting future ahead, whether it uses Pint/Parrot as its new engine or some form of the Zend Engine—or something altogether different. The truth of the matter remains that talk about PHP on Parrot is mere conjecture for the project as a whole. It is still interesting, nonetheless, and will be a development to watch with anticipation in the coming months and years.

For now, PHP 5 is here and will be with us for a long time. Happy coding!

Table 1. Mandelbrot fractal generation times (in seconds)
Round PHP Pint/Parrot
1 1.074 0.667
2 1.560 0.690
3 1.083 0.681
4 1.072 0.690
5 1.085 0.696
6 1.103 0.686
7 1.079 0.681
8 1.116 0.686
9 1.113 0.692
10 1.106 0.681
11 1.118 0.674
12 1.125 0.693
13 1.076 0.701
14 1.062 0.683
15 1.084 0.685
Average 1.124 0.686
Listing 2. Pint-generated IMC for mandelbrot fractal
.sub _main
$P0 = new PerlUndef
$P1 = new PerlUndef
$P2 = new PerlUndef
$P3 = new PerlUndef
$P4 = new PerlUndef
$P5 = new PerlUndef
$P6 = new PerlUndef
$P7 = new PerlUndef
$P8 = new PerlUndef
$P9 = new PerlUndef
$P10 = new PerlUndef
$P11 = new PerlUndef
$P12 = new PerlUndef
$P13 = new PerlUndef
$P14 = new PerlUndef
$P15 = new PerlUndef
$P16 = new PerlUndef
$P17 = new PerlUndef
$P18 = new PerlUndef
.sym PerlUndef b
b = new PerlUndef
b = " .:,;!/>)|&IH%*#"
.sym PerlUndef y
y = new PerlUndef
y = 30
L0:
$P0 = y * 0.1
$P1 = $P0 - 1.5
.sym PerlUndef C
C = new PerlUndef
C = $P1
$P2 = clone y
y = y - 1
if $P2 goto L1
goto L2
L1:
print "\n"
.sym PerlUndef x
x = new PerlUndef
x = 0
L3:
$P3 = x * 0.04
$P4 = $P3 - 2
.sym PerlUndef c
c = new PerlUndef
c = $P4
.sym PerlUndef z
z = new PerlUndef
z = 0
.sym PerlUndef Z
Z = new PerlUndef
Z = 0
$P5 = clone x
x = x + 1
$I0 = 0
$P19 = new PerlUndef
$P19 = 75
unless $P5 < $P19 goto L6
$I0 = 1
L6:
if $I0 goto L4
goto L5
L4:
.sym PerlUndef r
r = new PerlUndef
r = clone c
.sym PerlUndef i
i = new PerlUndef
i = clone C
.sym PerlUndef k
k = new PerlUndef
k = 0
L7:
$P6 = z * z
$P7 = Z * Z
$P8 = $P6 - $P7
$P9 = $P8 + r
.sym PerlUndef t
t = new PerlUndef
t = $P9
$P20 = new PerlUndef
$P20 = 2
$P10 = $P20 * z
$P11 = $P10 * Z
$P12 = $P11 + i
Z = $P12
z = clone t
$I1 = 0
$P21 = new PerlUndef
$P21 = 50
unless k < $P21 goto L10
$I1 = 1
L10:
if $I1 goto L8
goto L9
L8:
$P13 = z * z
$P14 = Z * Z
$P15 = $P13 + $P14
$I2 = 0
$P22 = new PerlUndef
$P22 = 10
unless $P15 > $P22 goto L13
$I2 = 1
L13:
if $I2 goto L11
goto L12
L11:
goto L9
L12:
$P16 = clone k
k = k + 1
goto L7
L9:
$P17 = k % 16
typeof $S0, b
if $S0 == "OrderedHash" goto L15
if $S0 == "PerlString" goto L14
goto L16
L14:
$S1 = b
$I3 = $P17
$S2 = $S1[$I3]
$P18 = $S2
goto L16
L15:
$P18 = b[$P17]
goto L16
L16:
print $P18
goto L3
L5:
goto L0
L2:
end
.end
  1. Simon Cozens, “Programming Parrot,” Perl.com, 1 Apr. 2001: <http://www.perl.com/pub/a/2001/04/01/parrot.htm>.

  2. Sterling Hughes and Thies C. Arntzen, “PHP and Parrot,” Closing Keynote, PHPCon West, Biltmore Hotel, Santa Clara, CA, 24 Oct. 2003: <http://web.archive.org/web/20040329013342/http://www.edwardbear.org/pap.pdf>. 2

  3. Robert Adkins, “Interview: Rasmus Lerdorf, PHP Guru,” Technetra, 21 Sep. 2004: <http://web.archive.org/web/20041012150441/http://www.technetra.com/Writings/recent/interview_lerdorf_html/view>. 2