Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107750 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 87334 invoked from network); 2 Nov 2019 23:55:42 -0000 Received: from unknown (HELO php-smtp3.php.net) (208.43.231.12) by pb1.pair.com with SMTP; 2 Nov 2019 23:55:42 -0000 Received: from php-smtp3.php.net (localhost [127.0.0.1]) by php-smtp3.php.net (Postfix) with ESMTP id B1BFB2D20C8 for ; Sat, 2 Nov 2019 14:44:26 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp3.php.net X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_HELO_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS3265 194.109.0.0/16 X-Spam-Virus: No Received: from lb1-smtp-cloud8.xs4all.net (lb1-smtp-cloud8.xs4all.net [194.109.24.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp3.php.net (Postfix) with ESMTPS for ; Sat, 2 Nov 2019 14:44:25 -0700 (PDT) Received: from [IPv6:2001:983:6fc5:1:e8d2:b8b5:e2bf:8f89] ([IPv6:2001:983:6fc5:1:e8d2:b8b5:e2bf:8f89]) by smtp-cloud8.xs4all.net with ESMTPA id R1C1iuJR1TzKrR1C3iefMQ; Sat, 02 Nov 2019 22:44:24 +0100 To: Nikita Popov , Dmitry Stogov Cc: PHP internals References: Message-ID: <36bfc10b-1adb-f67c-894e-8b6fc270e5fd@xs4all.nl> Date: Sat, 2 Nov 2019 22:44:21 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.9.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit X-CMAE-Envelope: MS4wfIcWlBCPQBULibjRa/HVL4H3HMoR54Z3xyLSKS1Oi1L2g6+mzmBjihBWj4yDH73arUPiRCPKNI+8qAJE6YELNmFFjFFPD9zIeAz/5UOAqQ/Fnt8h5P2u rgkvsOqOY4ZNPT309AGJe69wAIl0VAit99sY9kb0SmTYmy7MY5i8ITdWKouD3kyVc+PKxghznChhQM1nCIkYZQtvpxS/6X/tlMYoXauNh5GZXMB1KBzI7bIi kNRhvAcVikUqSuZP+WUlORhv42QsC0awmNH7brkRBnErVWxT2aV5oS8Z+V69yVnxNyfyUnZhwtIyodJCRnkvwgAAj/uO64/sQrnwLWF7MiA= X-Envelope-From: Subject: Re: [PHP-DEV] Re: [RFC] Union Types v2 From: d.takken@xs4all.nl (Dik Takken) On 25-10-19 12:22, Nikita Popov wrote: > For reference, here are the results I get with/without JIT: > https://gist.github.com/nikic/2a2d363fffaa3aeb251da976f0edbc33 I toyed a bit with the benchmark script (union_bench.php) as well and wanted to share some observations. First of all I noticed the benchmark script has a typo on line 90 where it is calling the wrong function. It should read: func6(1, 2, 3, 4, 5); When running the corrected script I see that adding 5 argument type checks and a return type check cause almost 4x slowdown. My results (with opcache / jit): func($a,$b,$c,$d,$e) 0.680 0.583 func(int $a,$b,$c,$d,$e): int 2.106 2.009 However, this appears to be entirely due to the return type check lacking a JIT implementation, as pointed out by Nikita. Adding one more test to the benchmark shows this nicely: func($a,$b,$c,$d,$e) 0.675 0.575 func(int $a,$b,$c,$d,$e) 0.574 0.475 func(int $a,$b,$c,$d,$e): int 2.106 2.009 Now we can see that the argument type hint actually improves performance, I guess due to it narrowing down the number of possible types that need to be considered for the function arguments. Union types allow for more accurate type hinting as well as type hinting in places where this is currently not possible. As a result union types can be used to obtain performance gains. As an example, consider the case where the return type hint matches the type information that opcache has inferred about the variable that is returned. In that case, the return type check is optimized away. Let us try and leverage union types to make this happen. From the benchmark script we take func6: function func6(int $a, int $b, int $c, int $d, int $e) : int { return $a + $b + $c + $d + $e; } and adjust it to read: function func6(int $a, int $b, int $c, int $d, int $e) : int|float { return $a + $b + $c + $d + $e; } Now the return type hint matches what opcache infers the result of the expression will be and the cost of return type checking disappears completely: func($a,$b,$c,$d,$e) 0.663 0.568 func(int $a,$b,$c,$d,$e) 0.574 0.475 func(int $a,$b,$c,$d,$e): int|float 0.561 0.466 Then, on to another observation. The SSA forms currently produced by opcache show union types like string|int. This suggests that opcache supports union types for type inference already. It explains why opcache can nicely optimize type checks away even when union types are used. This is not true for unions of classes though. A union type like int|Foo copies into the SSA form just fine while Foo|Bar becomes 'object'. Code like this: class Foo {} class Bar {} function func(): Foo|Bar { return new Foo(); } func(); produces the following SSA form: func: ; (lines=4, args=0, vars=0, tmps=1, ssa_vars=2, no_loops) ; (before dfa pass) ; /php-src/sapi/cli/test.php:6-8 ; return [object] BB0: start exit lines=[0-3] ; level=0 #0.V0 [object (Foo)] = NEW 0 string("Foo") DO_FCALL VERIFY_RETURN_TYPE #0.V0 [object (Foo)] -> #1.V0 [object] RETURN #1.V0 [object] which will still perform a return type check even though the return type hint matches the actual type of the variable. Apparently the union type support in opcache is present but incomplete. So, while union types can incur higher type checking cost they also provide more powerful means to help type inference and improve performance. As opcache improves over time I think we can expect the cost to decrease while the gain increases. Or am I too optimistic here? Regards, Dik Takken