> -----Original Message-----
> From: Jefferson Gonzalez [mailto:jgm...@gmail.com]
> Sent: Sunday, February 22, 2015 4:25 PM
> To: Etienne Kneuss; Anthony Ferrara; Zeev Suraski
> Cc: PHP internals
> Subject: Re: [PHP-DEV] Coercive Scalar Type Hints RFC
>

Jefferson,

Please note that Anthony, the lead of the Dual Mode RFC, said this earlier
on this thread, referring to the claim that Strict STH can improve JIT/AOT
compilation:

"A statement which I said on the side, and I said should not impact RFC or
voting in any way. And is in no part in my RFC at all."

Please also see:

marc.info/?l=php-internals&m=142439750614527&w=2

So while Anthony and I don't agree on whether there are performance gains
to be had from Strict STH, both of us agree that it's not at a level that
should influence our decision regarding the RFCs on the table.

I wholeheartedly agree with that stance, which is why I also listed the
apparently extremely widespread misconception (IMHO) that Strict STH can
meaningfully help JIT/AOT in my RFC.

Despite that, as your email suggests, there are still (presumably a lot)
of people out there that assume that there are, in fact, substantial gains
to be had from JIT/AOT if we introduce Strict STH.  I'm going to take
another stab at explaining why that's not the case.

> A JIT or AOT machine code generator IMHO will never have a decent use of
> system resources without some sort of strong/strict typed rules,
somebody
> explain if thats not the case.

It kind of is and kind of isn't.

There's consensus, I think, that if PHP was completely strongly typed -
i.e., all variables need to be declared and typed ahead of time, cannot
change types, etc. - we'd clearly be able to create a lot of optimizations
in AOT that we can't do today.  That the part that 'is the case'.  But
nobody is suggesting that we do that.  The discussion on the table is
very, very narrow:

-- Can the code generated for a strict type hint can somehow be optimized
significantly better than the code generated for a dynamic/coercive type
hint.

And here, I (as well as Dmitry, who actually wrote a JIT compiler for PHP)
claim that it isn't the case.  To be fair, there's no consensus on this
point.

Let me attempt, again, to explain why we don't believe there are any gains
associated with Strict STH, be them with the regular engine, JIT or AOT.

Consider the following code snippet:

function strict_foo($x)
{
  if (!is_int($x)) {
    trigger_error();
  }
   .inner_code.
}

function very_lax_foo($x)
{
    $x = (int) $x;
   .inner_code.
}

function test_strict()
{
  .outer_code.
  strict_foo($x);
}

function test_lax()
{
  .outer_code.
  very_lax_foo($x);
}

test_strict();
test_lax();


strict_foo() implements a pretty much identical check to the one that a
Strict integer STH would perform.
very_lax_foo() implements an explicit type conversion to int, that can
pretty much never fail - which is significantly more lax than what is
proposed for weak types in the Dual Mode RFC, and even more so compared to
the Coercive STH RFC.
.inner_code. is identical between the two foo() functions, and
.outer_code. is identical between the two tester functions.

The claim that strict types can be more efficiently optimized than more
lax types, suggests it should be possible to optimize the code flow for
test_strict()/strict_foo() significantly better than for very_lax_foo()
using JIT/AOT.

Let's dive in.  Beginning with the easy part, that's been mentioned
countless times - it's clear that it's just as easy (or hard) to optimize
the .inner_code. block in the two implementations of foo().  It can bank
on the exact same assumptions - $x would be an integer.  So we can
optimize the two function bases to exactly the same level.  For example,
if we're sure that $x inside the function never changes type - it can be
optimized down to a C-level long.  That's oversimplifying things a bit,
but the important thing here is that it can be easily proven that the two
function bodies can be optimized to the exact same level, for better or
worse.  The only difference between them is how they handle non-integer
inputs;  The strict implementation errors out if it gets a non-integer
typed value, while the lax version happily accepts everything.  But that's
a functionality difference, not a performance one (i.e., if you want the
value to be accepted in the strict case, you'd manually conduct the
conversion before the call is made, or sooner - resulting in roughly the
same behavior and performance).

Now the slightly trickier part - the .outer_code. block.  What can we say
about the type of $x, without knowing what code is in there?  Not a whole
lot.  We know that if $x isn't going to be of type int at the end of this
block, test_strict() is going to fail, but that doesn't mean $x will truly
be an int.  The fact I want to be young and healthy doesn't mean I'm going
to magically become young and healthy :)

Let's dive further.  Assuming we don't have strong variable type
declarations (i.e., int $x;  $x = "foo"; // fails!), there are two
possible outcomes from analyzing the .outer_code. block:

1. We can infer (with varying levels of confidence) that $x is going to be
an int right before the call to foo() is made.  Whether we can infer that
or not has nothing to do with any implementation detail of foo(),
including whether it's using Strict type hints, Weak type hints, or
conducts nuclear simulations.  It has only to do with the code in
.outer_code., which means we can do it equally well (or not so well) in
both test_strict() and test_lax().  Before anybody asks, the levels of
confidence would also not vary between the two flavors, and it too, has
only to do with what's written in the .outer_code. block.

2. We cannot determine what type $x is going to be right before the call
to foo() is made.  Here too, our ability or inability to determine that is
identical between test_strict() and test_lax(), and has only to do with
our ability to analyze .outer_block., nothing else.

Now, let's continue to dive into the first scenario, as the second one can
obviously not be optimized in any meaningful way.

To simplify things, let's assume we can know - with absolute confidence,
that $x is an int right before the call.  Can we somehow optimize
test_strict()/strict_foo() better than test_lax()/very_lax_foo()?  The
answer is - no, not really.  We could optimize them down to the exact same
machine level code - bypassing the is_strict() check in the strict_foo()
case, and the explicit cast in very_lax_foo() case.  With absolute
confidence that $x is an int, we could have a single long pass the
caller-callee boundary in both cases.  This is easier said than done -
JIT/AOT are quite complex - but it's equally hard in both cases, and the
end result is identical.

> As I see it, some example, if the JIT generated C++ code to then
generate the
> machine code:
>
> function calc(int $val1, int $val2) : int {return $val1 + $val2;}

How does that handle the situation where $val1 is a float?  Or a string?
Or an array?
Here you are already assuming that you KNOW $val1 and $val2 are ints, but
nothing about declaring $val1/$val2 as as 'strict int' implies anything
regarding the value/type that will actually be passed to the function.

> On weak mode I see the generated code would be something like this:
>
> Variant* calc(Variant& val1, Variant& val2) {
>      if(val1.isInt() && val2.isInt())
>          return new Variant(val1.toInt() + val2.toInt());
>
>      else if(val1.isFloat() && val2.isFloat())
>          return new Variant(val1.toInt() + val2.toInt());
>      else
>          throw new RuntimeError();
> }

Technically it'd be more like this:

Variant* calc(Variant& val1, Variant& val2) {
   if(val1.isInt() ) {
        // type checking
        if (!val1.coerceToInt()) {
          throw new RuntimeError()
        }
        If (!val2.coerceToInt()) {
          throw new RuntimeError();
        }

        // function body begins here
        int result = Variant(val1.intValue() + val2.intValue());
        return result;
}

But the code that's generated for strict typing would actually not look
very different, if it can't assume that val1 & val2 are ints.  A generic
implementation that has no insight about the incoming values would look
almost identical:

Variant* calc(Variant& val1, Variant& val2) {
   if(val1.isInt() ) {
        // type checking
        if (!val1.isInt()) {
          throw new RuntimeError()
        }
        If (!val2.isInt()) {
          throw new RuntimeError();
        }

        // function body begins here
        int result = Variant(val1.intValue() + val2.intValue());
        return result;
}

Between both cases, the actual code body (after the 'function body begins
here' comment), can be optimized in exactly the same way, and probably all
the way down to this:

{
  long val1, val2;

  return val1+val2;
}

In the strict case, if we somehow know ahead of time or just in time that
val1.isInt() and val2.isInt() are true (for a particular instance of a
call to calc()) - we can create a more efficient function calling code
that will do away with all of type checking, and go directly to the
function body.

But is the Dynamic version any different?  Not at all.
In fact, if val1.isInt() and val2.isInt() are true, we can equally bypass
the coerceToInt() calls in the dynamic version - as we already know it's
an int!  What are we left with?  The exact same code, down to the last
bit.

To summarize, the difference between the different flavors of STH simply
has no meaningful impact on the performance of the current non-JITted PHP
as well as potential future JIT/AOT engines.  If you can infer the caller
argument types - you can reach the same level of performance in all the
different types of STH.  If you can't infer the types - the code would
look remarkably similar between the two, with the big difference being
behavioral - not performance.  All types of STH give you the exact same
input for optimizing the callee, down to the last bit.

> 1. Does weak mode could provide the required rules to implement a JIT
with
> a sane level of memory and CPU usage?

It would use the exact same amount of memory and CPU as strict mode.  The
hard part remains inferring types - and as I hoped I illustrated, there's
no different between different flavors of STH in that front.

> 2. I see that the proponents of dual weak/strict modes are offering to
write a
> AOT implementation if strict makes it, And the impresive work of Joe
(JITFU)
> and Anthony on recki-ct with the strict mode could be taken to another
level
> of integration with PHP and performance. IMHO is harder and more
resource
> hungry to implement a JIT/AOT using weak mode. With that said, if a JIT
> implementation is developed will the story of the ZendOptimizer being a
> commercial solution will be repeated or would this JIT implementation
would
> be part of the core?

Actually, what I'm seeing is the proponents of dual weak/strict mode
saying that JIT/AOT should not be a part of the discussion around the
RFCs, and people refusing to accept that! :)
But more importantly, as early as when we announced PHPNG, we said we'd
want to look into JIT solutions once it ships.  We'd obviously want to
cooperate with everyone interested to try and create the best JIT
implementation possible once PHP 7 is out the door, as a part of an open
effort.  If it's ever any good, it'll make it into the core, if accepted.

> Thats all that comes to mind now, and while many people doesn't care for
> performance, IMHO a programming language mainly targeted for the web
> should have some caring on this department.

I think it's fair to say that Dmitry - who led the PHPNG effort - cares *a
lot* performance.  I'm sure you'd agree.  I tend to think that I also care
a lot about performance, and so does Xinchen.  We all spent substantial
parts of our lives working to speed PHP up.  It's not whether we think
performance is important - it is (although we do believe we should build
optimizers for languages, more so than languages for optimizers).  It's
just that we all fail to see how the flavor of STH can have any meaningful
influence on performance.

Thanks for the feedback!

Zeev

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to