
Deep Dive into PHP 8's JIT - nawarian
https://thephp.website/en/issue/php-8-jit/
======
jashmatthews
This article is actually now quite out of date as PHP has switched to a
tracing JIT which appears to be heavily based on LuaJIT.

[https://github.com/php/php-src/pull/5874](https://github.com/php/php-
src/pull/5874)

[https://github.com/php/php-
src/commit/4bf2d09edeb14467ba7955...](https://github.com/php/php-
src/commit/4bf2d09edeb14467ba79551a08d84efdff314899)

~~~
pansa2
Any ideas why they’ve moved from a method-based JIT to tracing?

AFAIK tracing JITs are generally inferior to method-based ones, which is why
none of the major JavaScript engines use tracing. Their only advantage seems
to be (relative) simplicity, which is essential for the lightweight LuaJIT but
not for PHP.

~~~
MaxBarraclough
That doesn't sound right. How would a trace-based JIT be easier to implement?
There was a research project which made HotSpot into a trace-based JIT. It
reported both faster compilation times, and superior quality of generated
code.

(I don't know if the HotSpot folks ever considered adopting the changes.)

[http://www.ssw.jku.at/Research/Papers/Haeubl11/](http://www.ssw.jku.at/Research/Papers/Haeubl11/)

~~~
pansa2
> How would a trace-based JIT be easier to implement?

When compiling at method-granularity, with incomplete information about types,
complex features are required e.g. On-Stack Replacement (if the current method
becomes "hot", it needs to be replaced by a compiled version), Inline Caches,
Hidden Classes and Deoptimization.

In a trace-based JIT, all these features can be replaced by "just compile
another trace".

~~~
jashmatthews
We need all those complex features with tracing JITs too. Getting into a trace
is basically OSR. Exiting a trace and restoring the interpreter state is
basically an OSR exit or deoptimization. It's really just different
terminology for the same thing. Inline caches on method calls become guards.
We can't just ignore them.

LuaJIT does without hidden classes or property caching because on-trace it's
able to eliminate table access and allocation. Performance tanks on OO Lua
once you fall off trace. You can work around it with "compile another trace"
in a sense that works as long as you have infinite cache.

------
untog
Tiny nitpick: the article describes JavaScript as an interpreted language
without JIT when most major JS engines do have JIT optimisation. It’s
difficult to make this call about any language with more than one
implementation.

But anyway, kudos to the PHP folks for yet another improvement. It’s been a
long time since I used it but I’m continually impressed by how far it’s come.
And it’s a useful test to how flexible a developer is willing to be to ask
them to code some PHP, there are fewer and fewer reasons against it these days
other than personal style preference.

~~~
viraptor
The whole idea of "language X is interpreted/compiled" is a simplification I
wish people were more careful with.

Cpython is interpreted, pypy has JIT, mypyc is compiled. It's the
implementation, or even implementation's specific runtime options that decide
about things like that, not the language.

~~~
userbinator
The statement would be far more accurate as "language X is [almost
always/sometimes/often/commonly/etc.] interpreted/compiled". Unfortunately a
lot of people seem to like speaking in absolutes.

C can be interpreted too:
[https://en.wikipedia.org/wiki/CINT](https://en.wikipedia.org/wiki/CINT)

~~~
colejohnson66
Theoretically, any language can be interpreted.

Compilation is just _interpreting_ what a program does _and_ producing machine
code (or other code in the case of transpilers) that computes the same thing.

Interpreters are just that, but instead of _producing_ code, they run code in
themselves thats computes the results directly.

One could even consider machine code just “obfuscated” assembly code. In that
sense, machine code is just another language that _also_ could be interpreted
(intepretation-based emulators like Bochs) or compiled again (JIT-based
emulators like DOSBox).

This brings up a slightly related question: is there a language that _can’t_
be _compiled_?

~~~
tyingq
_" question: is there a language that can’t be compiled?"_

There is some question whether Perl could be, because parsing it without
running it has some ambiguity.
[https://www.perlmonks.org/?node_id=663393](https://www.perlmonks.org/?node_id=663393)

~~~
viraptor
That's not really a barrier. It's "can't be parsed without ambiguity", not
"can't be parsed". You compile cases like that by compiling a check for the
relevant condition, then compile the possible versions in the if/else
branches. Unless you can prove which case it will be from other code - then
you can simplify anyway.

------
freelancercv
"PHP as fast as C" \- People who are in php language development may die
laughing reading this statement.

Truth can be harsh, but people sometime overvalue to such an extent is hard to
understand.

~~~
chubot
What I've noticed is that JITs often can reach C speed in workloads like this:

    
    
        sum = 0
        for i in xrange(n):
          for j in xrange(i):
            sum += A[i][j]
    

And when they reach that milestone, some people call it "as fast as C".

Never mind that that's not what people actually write in Python or PHP. It's a
synthetic benchmark, not a real workload.

The workloads in those languages are generally oriented around strings, hash
tables, and function/method calls.

And the JITs don't seem to do nearly as good a job there. I tested PyPy on Oil
[1] a few years ago, and it made it slower, not faster. And it used more
memory. (Though PyPy is an amazing project in many respects.)

[1] [https://www.oilshell.org](https://www.oilshell.org)

~~~
remram
This is not what people write in Python or PHP, but this is what people write
in C extensions for Python or PHP. Having your JIT be that fast allows you to
forego those extensions and write the low-level hot loops in the same
language, and that's a huge improvement.

You usually don't care how your matrix multiplication/regex matching/unicode
normalization/JSON parsing is implemented, but people had to make those, and
they are users of the language too.

Even though it might not change the bottom-line for your high-level app.

~~~
chubot
Well, the problem is that Python and PHP are actually bad languages for
expressing code like that. For expressing C. For one, they're not statically
typed.

Julia is a dynamic language that seems to do better because it was designed
for this purpose.

But it doesn't seem to have panned out in practice in Python, or PHP as far as
I know. Those languages have huge piles of C, and whenever you call into C,
the JIT gets confused. People don't seem to rewrite their huge piles of C in
Python or PHP. In Python, it's more likely Cython.

I'd like to see pointers to counterexamples -- where people actually wrote
some C-like code in Python or PHP and let the JIT do its work. I haven't seen
it, aside from the PyPy project itself, and maybe a few other examples. I
think you would still take a significant performance hit.

The issue is that C compilers in 2020 are _even better_ at compiling the
example I showed. They do amazing things with that kind of code that state-of-
the-art JITs don't in practice.

~~~
aardvark179
It’s not that the JIT gets confused, it’s that the C APIs for these languages
can do almost anything - even stuff that you can’t normally do in the
language. So you are faced with a giant optimization boundary.

However a call to a shared library that isn’t linked against your language API
is not very expensive as you have a much better handle on the values that are
escaping and can make much better optimization choices.

In the Truffle project we are using an LLVM bitcode interpreter that allows us
to JIT right through that language boundary and still link to native shared
libraries. This means people shouldn’t have to rewrite their C extensions and
we can hopefully still run the combination of high level language and C
extension faster.

~~~
jashmatthews
That optimization boundary seems like it's much more of a problem for
TruffleRuby than it is for language-specific native implementations? IIRC
TruffleRuby relies a lot on being able to optimize away Ruby objects and
frames and there's quite a performance cliff if you have to materialize full
escaping objects?

JSC and LuaJIT have simpler ways to deal with calling native code which might
do weird stuff.

------
pjmlp
Unless we are speaking about Java before Java 1.2, it is definitely not
interpreted, there are plenty of JIT and AOT implementations without any kind
of interpretation step.

Since 25 years, time to learn that implementations and languages are not the
same.

~~~
nawarian
I might be wrong here, as I'm not so close to Java development. But a language
implementing JIT, at least to me, is interpreted.

Could you please point an implementation detail where a JIT-capable engine
doesn't include interpretation in its runtime?

In every case, thanks a lot for your feedback!

~~~
pjmlp
Well, from CS compiler theory point of view it is not.

For example in .NET, MSIL goes directly into a pipeline that produces native.
You can easily validate that RyuJIT has no interpretation.

Or for example, watchOS applications packaged with bitcode, get JIT compiled
at installation time.

------
fetbaffe
There is a small error in the text. When describing the opcache.jit setting it
shows examples where the third flag is set to 5, eg 1255, but the table of
possible values for the third flag, 'T - JIT trigger', goes from 0 to 4.

~~~
nawarian
Thanks a lot! I blindly fetched this from the reference mentioned there. I'll
update this as soon as I have time (probably during this week) and also let
the author of the referenced post know about it.

------
google234123
Didn't Facebook also develop is JIT for their PHP? Or have they moved on frmo
PHP?

~~~
tyingq
Hack/HHVM no longer has a goal to be PHP compatible, but it looks pretty much
the same. It does have a JIT: [https://hhvm.com/blog/2027/faster-and-cheaper-
the-evolution-...](https://hhvm.com/blog/2027/faster-and-cheaper-the-
evolution-of-the-hhvm-jit)

~~~
ksec
Do we know anyone outside of Facebook uses Hack/HHVM in production?

~~~
nym3r0s
Wikipedia runs on HHVM IIRC. [https://hhvm.com/blog/7205/wikipedia-on-
hhvm](https://hhvm.com/blog/7205/wikipedia-on-hhvm)

~~~
njuw
Wikipedia was moved back to PHP:

\-
[https://phabricator.wikimedia.org/T176370](https://phabricator.wikimedia.org/T176370)

\-
[https://phabricator.wikimedia.org/T229792](https://phabricator.wikimedia.org/T229792)

\- [https://launchdarkly.com/blog/how-the-wikimedia-
foundation-s...](https://launchdarkly.com/blog/how-the-wikimedia-foundation-
successfully-migrated-to-php7/)

~~~
nym3r0s
Didn't know that. Thanks for the info!

------
ksec
May be everyone on HN has Adblock/ PiHole on.

But this Blog, a single page has 5 Google Ads in it. I dont mind one or two,
top and bottom. But 5, right in the middle of every section.

~~~
geek_at
Blogs like these are the main reason to use ad blocks. And from their
perspective: everyone using adblocks is the reason they need 5 ads on the site

~~~
nawarian
Hey man, author here. Thanks for the feedback. I recently added adsense there
and I'm testing with the amount of ads in the page. Currently I'm letting
AdSense decide how many ads it should place and where.

But if you believe the amount is so harmful for your experience, don't worry.
I'll be more than glad to reduce this amount.

Cheers!

------
nawarian
Thank you all for sharing your comments and thoughts. I'm unfortunately out of
time today, but I'll try to address your comments asap :)

Cheers!

------
rurban
Not a deep dive, a rather very shallow dive.

Deep dives were handled before:
[https://wiki.php.net/rfc/jit](https://wiki.php.net/rfc/jit) And its
discussion
[https://externals.io/message/103903](https://externals.io/message/103903)

------
CountHackulus
I definitely misread this title as the P8 JIT that IBM was working on for a
while.

