PHP 8 is coming:
the new features

by Enrico Zimuel
Co-founder PUG Torino

About me

PUG Torino

PHP 8

New features (some)

  • JIT compiler (RFC)
  • Union types (RFC)
  • Static return type (RFC)
  • Weak maps (RFC)
  • ::class on objects (RFC)
  • Stringable interface (RFC)

JIT

What's JIT?

    A Just-In-Time (JIT) compiler is a feature of the run-time interpreter, that instead of interpreting bytecode every time a method is invoked, will compile the bytecode into machine code, and then invoke this object code instead

PHP execution

Example


for ($i=0; $i<100; $i++) {
    echo $i;
}	

Opcode:


L0 (2):     ASSIGN CV0($i) int(0)
L1 (2):     JMP L4
L2 (3):     ECHO CV0($i)
L3 (2):     PRE_INC CV0($i)
L4 (2):     T1 = IS_SMALLER CV0($i) int(100)
L5 (2):     JMPNZ T1 L2
L6 (5):     RETURN int(1)

php -d opcache.opt_debug_level=0x20000 -d opcache.enable_cli=1 test.php

Opcache

JIT

JIT machine code


	sub $0x10, %rsp
	lea 0x50(%r14), %rdi
	cmp $0xa, 0x8(%rdi)
	jnz .L1
	mov (%rdi), %rdi
	cmp $0x0, 0x18(%rdi)
	jnz .L6
	add $0x8, %rdi
.L1:
	test $0x1, 0x9(%rdi)
	jnz .L7
	mov $0x0, (%rdi)
	mov $0x4, 0x8(%rdi)
.L2:
	mov $EG(exception), %rax
	cmp $0x0, (%rax)
	jnz JIT$$exception_handler
	jmp .L4
.L3:
	mov $0x7fcc67e2e630, %r15
	mov $0x561f82773690, %rax
	call *%rax
	mov $EG(exception), %rax
	cmp $0x0, (%rax)
	jnz JIT$$exception_handler
	cmp $0x4, 0x58(%r14)
	jnz .L9
	add $0x1, 0x50(%r14)
.L4:
	mov $EG(vm_interrupt), %rax
	cmp $0x0, (%rax)
	jnz .L11
	cmp $0x4, 0x58(%r14)
	jnz .L12
	cmp $0x64, 0x50(%r14)
	jl .L3
.L5:
	mov $0x7fcc67e2e6b0, %r15
	add $0x10, %rsp
	mov $ZEND_RETURN_SPEC_CONST_LABEL, %rax
	jmp *%rax
	sub $0x10, %rsp
	jmp .L4
.L6:
	mov $0x7fcc67e2e5c0, %rsi
	mov %r15, (%r14)
	mov $zend_jit_assign_const_to_typed_ref, %rax
	call *%rax
	jmp .L2
.L7:
	mov (%rdi), %rax
	mov %rax, (%rsp)
	mov $0x0, (%rdi)
	mov $0x4, 0x8(%rdi)
	mov (%rsp), %rdi
	sub $0x1, (%rdi)
	jnz .L8
	mov %r15, (%r14)
	mov $rc_dtor_func, %rax
	call *%rax
	jmp .L2
.L8:
	mov 0x4(%rdi), %eax
	and $0xfffffc10, %eax
	cmp $0x10, %eax
	jnz .L2
	mov $gc_possible_root, %rax
	call *%rax
	jmp .L2
.L9:
	mov %r15, (%r14)
	lea 0x50(%r14), %rdi
	cmp $0xa, 0x8(%rdi)
	jnz .L10
	mov (%rdi), %rsi
	cmp $0x0, 0x18(%rsi)
	lea 0x8(%rsi), %rdi
	jz .L10
	mov $0x0, %rdx
	mov $zend_jit_pre_inc_typed_ref, %rax
	call *%rax
	mov $EG(exception), %rax
	cmp $0x0, (%rax)
	jnz JIT$$exception_handler
	jmp .L4
.L10:
	mov $increment_function, %rax
	call *%rax
	jmp .L4
.L11:
	mov $0x7fcc67e2e670, %r15
	jmp JIT$$interrupt_handler
.L12:
	cmp $0x5, 0x58(%r14)
	jnz .L13
	mov $0x64, %rax
	vcvtsi2sd %rax, %xmm0, %xmm0
	vucomisd 0x50(%r14), %xmm0
	ja .L3
	jmp .L5
.L13:
	mov $0x7fcc67e2e670, %rax
	mov %rax, (%r14)
	lea 0x50(%r14), %rsi
	mov $0x7fcc67e2e5d0, %rdx
	lea 0x60(%r14), %rdi
	mov $compare_function, %rax
	call *%rax
	mov $EG(exception), %rax
	cmp $0x0, (%rax)
	jnz JIT$$exception_handler_undef
	cmp $0x0, 0x60(%r14)
	jl .L3
	jmp .L5

php -d opcache.enable_cli=1 -d opcache.jit=1235
-d opcache.jit_buffer_size=64M -d opcache.jit_debug=1 test.php

How does JIT choose to generate machine code?

JIT configuration (CRTO)

  • opcache.jit consists of 4 decimal digits, e.g 1235
  • 1 = use AVX instructions (CPU optimization)
  • 2 = Register allocation
  • 3 = profile on the fly and compile hot functions (Trigger)
  • 5 = type inference & procedure analysis (Optimization level)

JIT Trigger

  • 0 = JIT all functions on first script load
  • 1 = JIT functions on first execution
  • 2 = Profile on first request and compile hot functions on second request
  • 3 = Profile on the fly and compile hot functions
  • 4 = Compile functions with @jit tag in doc-comments

JIT benchmark

Execution time (sec) of Zend/bench.php, using PHP 7.4.4 (Zend Engine 3.4.0)
and PHP 8.0.0-dev (Zend Engine 4.0.0-dev)

JIT on WordPress

  • PHP 8 no JIT: 190 req/sec
  • PHP 8 + JIT (CTRO 1235): 160 req/sec
  • PHP 8 + JIT (CTRO 1225): 189 req/sec

Source: PHP 8 et Just In Time Compilation

IO Bound vs. CPU Bound

  • JIT is useful for CPU intensive application (machine learning, image manipulation, etc).
  • JIT does not offer improvement for IO Bound application

Union types

Union types (RFC)


class Number {
    private int|float $number;
	
    public function setNumber(int|float $number): void {
        $this->number = $number;
    }
	
    public function getNumber(): int|float {
        return $this->number;
    }
}

Static return type

Static return type (RFC)


class A {
    public function __construct($params = []) {
        $this->params = $params;
    }

    public function create($params): static {
        return new static($params);
    }
}
class B extends A {
}

$b = new B();
$result = $b->create(['x' => 'y']);
var_dump($result); // object(B) {["params"]=> ["x" => "y"]}

Fluent interfaces


class A {
    public function doWhatever(): static {
        // Do whatever.
        return $this;
    }
}
$a = new A();
$result = $a->doWhatever(); // $result === $a

Weak maps

Weak maps (RFC)

To provide "weakly referenced" map objects


$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;
var_dump($map);
// object(WeakMap)#1 (1) {
//   [0]=> [ 
//     "key" => object(stdClass)#2,
//     "value" => int(42)
//   ]
// }
	
// The object is destroyed here, key automatically removed
unset($obj);
var_dump($map);
// object(WeakMap)#1 (0) { }	

Example: caching objects


class Foo {
    private WeakMap $cache;
	
    public function getSomethingWithCaching(object $obj) {
        return $this->cache[$obj] ??= $this->somethingExpensive($obj);
    }
}

??= Null Coalescing Assignment (PHP 7.4+)

::class on objects

::class on objects (RFC)


$object = new stdClass;
var_dump($object::class); // "stdClass"
	
$object = null;
var_dump($object::class); // TypeError

Stringable interface

Stringable interface (RFC)

					
interface Stringable
{
    public function __toString(): string;
}

Stringable interface is automatically added to classes that implement the __toString() method

PHP 8 Release date

Feature freeze: July 28th 2020

GA release: December 3rd 2020

References

Thanks!

Follow me: @ezimuel

Subscribe to PUG Torino on Meetup!



Creative Commons License
This work is licensed under a
Creative Commons Attribution-ShareAlike 3.0 Unported License.
I used reveal.js to make this presentation.