Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124719 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 734D31A00B7 for ; Fri, 2 Aug 2024 17:09:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1722618678; bh=d+UDRdBMWmAafMQcW9HeFOlmTexQNaphm/i7ZMQd72k=; h=Date:From:To:In-Reply-To:References:Subject:From; b=kHcPmiqxaYd41hXRxWldlgNigMI6TKkStKMShjI07/lLhTtj1ETY6as+ylPd9VesP 6aAbFF2Dfi2T/kTW/xtsURSOAhuhYhmBeVOxGjNRCT5Z2SFck8FytHH2cMmG0rUR7T aXkMlQnnNxyW8AudDgX2pcklvFeuBe+q4bgLLGB1cMUOHz1pf/4HCKfJ+Ncwd60YZn RdoXqVSJbR3QZy5H9LXH7ok5LYoJYfrflO/GL6YvFhV7CGDKyWfSzsvprMRPEpwV4w iN4fko+tBH71AJHG032VekoGz7zCZAa79QKbwp1rA4K6SQzqXmOhFvdWCGDS4piNaG UidgBey/Qd79A== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 18E5C18006F for ; Fri, 2 Aug 2024 17:11:17 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fhigh5-smtp.messagingengine.com (fhigh5-smtp.messagingengine.com [103.168.172.156]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Fri, 2 Aug 2024 17:11:16 +0000 (UTC) Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailfhigh.nyi.internal (Postfix) with ESMTP id 4E90611505BC for ; Fri, 2 Aug 2024 13:09:34 -0400 (EDT) Received: from imap49 ([10.202.2.99]) by compute3.internal (MEProxy); Fri, 02 Aug 2024 13:09:34 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm1; t=1722618574; x=1722704974; bh=pkvDCGc5Q/ 7TuIMj2eCPNORgf6IPpJb/IkIsxkeWKB8=; b=QMvoyF9NqkAOgKnyy0xqjkRoiL KI2Y2s8cKsWKLP0RDmpxWTjATrHQisoWSp8bmzw/lhDjVx0UGeW/FKUf8hmvX6RC oM7dxWnpOsFp26ALTzy9tKyhcoXKlnKWSbS2PFPFvV7X+L4rqKmYop8Pda1mQ9B4 jvOAOQAmeUIclJovrw+kVzU8Ss01HfHa3Kg+MxaSJwNHE6G81OC0vrNR/nZjPPeV 7ajBlDlzWAmcnejVhHWJ7YcviCz1ik6XNcQnabKcVtqtKeZuabyGWI5wOHEgHHRA dKWuh8Z9BJUkYtEwSU1rIa1fPbeyFzDyGj03ukVRga1ObikQxiGy8f/BqUNw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm3; t=1722618574; x=1722704974; bh=pkvDCGc5Q/7TuIMj2eCPNORgf6IP pJb/IkIsxkeWKB8=; b=J9pgR3ZzRmlOuWOKkzE81Wp+Lexb5PRi0Mg1HXSBBsZP sddOiWkHNqLWgGdhHVrc6w0iRRNsQdxF19M7CrnEza9EFLzqqYybJV9nHonjx3TC /y1hzk4nHZoHq1pjCNwQbKsA23XRR6imuNXW5xI10isT/NG5r1aE54aA92Df5Q1P 5tHa8ZqlI4GyDvrO6sUtGsidlJlhG7uN5weq6NbtcDzUSJoVD+9mlr1mmqo3Dv2W oM1RZLI+I6fn/OtEwyDnD6slx+1Af/PqacMfhK81QHM+Q3OfqXS/lv3nyhaizip4 F69giLTbt/ro2sPWbM6s4ifMktlKp1Ka8/xGXibh0g== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeftddrkedtgddutdelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucenucfjughrpefoggffhffvkfgjfhfutgesrgdtre erredtjeenucfhrhhomhepfdftohgsucfnrghnuggvrhhsfdcuoehrohgssegsohhtthhl vggurdgtohguvghsqeenucggtffrrghtthgvrhhnpeeitdfhhfdvfffhtedtgfevfefgue eggeduueekjeehieeggffhieevleeffeeufeenucffohhmrghinhepghhithhhuhgsrdgt ohhmnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprh hosgessghothhtlhgvugdrtghouggvshdpnhgspghrtghpthhtoheptd X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.nyi.internal (Postfix, from userid 501) id EA4C115A0092; Fri, 2 Aug 2024 13:09:33 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Fri, 02 Aug 2024 19:08:36 +0200 To: internals@lists.php.net Message-ID: In-Reply-To: References: Subject: Re: [PHP-DEV] [Concept] Flip relative function lookup order (global, then local) Content-Type: multipart/alternative; boundary=db986c3ee94548d495ad1297aca362ca From: rob@bottled.codes ("Rob Landers") --db986c3ee94548d495ad1297aca362ca Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Fri, Aug 2, 2024, at 18:51, Ilija Tovilo wrote: > Hi everyone >=20 > As you probably know, a common performance optimization in PHP is to > prefix global function calls in namespaced code with a `\`. In > namespaced code, relative function calls (meaning, not prefixed with > `\`, not imported and not containing multiple namespace components) > will be looked up in the current namespace before falling back to the > global namespace. Prefixing the function name with `\` disambiguates > the called function by always picking the global function. >=20 > Not knowing exactly which function is called at compile time has a > couple of downsides to this: >=20 > * It leads to the aforementioned double-lookup. > * It prevents compile-time-evaluation of pure internal functions. > * It prevents compiling to specialized opcodes for specialized > internal functions (e.g. strlen()). > * It requires branching for frameless functions [1]. > * It prevents an optimization that looks up internal functions by > offset rather than by name [2]. > * It prevents compiling to more specialized argument sending opcodes > because of unknown by-value/by-reference passing. >=20 > All of these are enabled by disambiguating the call. Unfortunately, > prefixing all calls with `\`, or adding a `use function` at the top of > every file is annoying and noisy. We recently got a feature request to > change how functions are looked up [3]. The approach that appears to > cause the smallest backwards incompatibility is to flip the order in > which functions are looked up: Check in global scope first, and only > then in local scope. With this approach, if we can find a global > function at compile-time, we know this is the function that will be > picked at run-time, hence automatically enabling the optimizations > above. I created a PoC implementing this approach [4]. >=20 > M=C3=A1t=C3=A9 has kindly benchmarked the patch, measuring an improvem= ent of > ~3.9% for Laravel, and ~2.1% for Symfony > (https://gist.github.com/kocsismate/75be09bf6011630ebd40a478682d6c17). > This seems quite significant, given that no changes were required in > either of these two codebases. So, what you=E2=80=99re saying is that symfony and laravel can get a per= formance increase by simply adding a \ in the right places? Why don=E2=80= =99t they do that instead of changing the language? >=20 > There are a few noteworthy downsides: >=20 > * Unqualified calls to functions in the same namespace would be > slightly slower, because they now involve checking global scope first. > I believe that unqualified, global calls are much more common, so this > change should still result in a net positive. It's also possible to > avoid this cost by adding a `use function` to the top of the file. For functions/classes in the same exact namespace, you don=E2=80=99t nee= d a use statement. But after this change, you do in certain cases? namespace Foo; function array_sum($bar) {} function baz($bar) { return array_sum($bar); } So, how do you use that function in the same file? > * Introducing new functions in the global namespace could cause a BC > break for unqualified calls, if the function happens to have the same > name. This is unfortunate, but likely rare. Since new functions are > only introduced in minor/major versions, this should be manageable, > but must be considered for every PHP upgrade. We can only see open source code when doing impact analysis. This means = picking even a slightly =E2=80=9Cpopular=E2=80=9D name could go very poo= rly. > * Some mocking libraries (e.g. Symfony's ClockMock [5]) intentionally > declare functions called from some file in the files namespace to > intercept these calls. This use-case would break. That said, it is > somewhat of a fragile approach to begin with, given that it wouldn't > work for fully qualified calls, or unnamespaced code. See above. I=E2=80=99ve seen this =E2=80=9Ctrick=E2=80=9D used on many c= losed source projects. I=E2=80=99ve also seen it used when PHP has a bug= and the workaround is to implement it in php like this.=20 >=20 > I performed a small impact analysis [6]. There are 484 namespaced > functions shadowing global, internal functions in the top 1000 > composer packages. However, the vast majority (464) of these functions > come from thecodingmachine/safe, whose entire purpose is offering > safer wrappers around internal functions. Excluding this library, > there are only 20 shadowing functions, which is surprisingly little. > Furthermore, the patch would have no impact on users of > thecodingmachine/safe, only on the library code itself. >=20 > As for providing a migration path: One approach might be to introduce > an INI setting that performs the function lookup in both local and > global scope at run-time, and informs the user about the behavioral > change in the future. To mitigate it, an explicit `use function` would > need to be added to the top of the file, or the call would need to be > prefixed with `namespace\`. The impact analysis [6] also provides a > script that looks for shadowing functions in your project. It does not > identify uses of these functions (yet), just their declarations. >=20 > Lastly, I've already raised this idea in the PHP Foundations internal > chat but did not receive much positive feedback, mostly due to fear of > the potential BC impact. I'm not particularly convinced this is an > issue, given the impact analysis. Given the surprisingly large > performance benefits, I was inclined to raise it here anyway. It also > sparked some related ideas, like providing modules that lock > namespaces and optimize multiple files as a singular unit. That said, > such approaches would likely be significantly more complex than the > approach proposed here (~30 lines of C code). >=20 > Anyway, please let me know about possible concerns, broken use-cases, > or any alternative approaches that may come to mind. I'm looking > forward to your feedback. >=20 > Ilija >=20 > [1] https://github.com/php/php-src/pull/12461 > [2] https://github.com/php/php-src/pull/13634 > [3] https://github.com/php/php-src/issues/13632 > [4] https://github.com/php/php-src/pull/14529 > [5] https://github.com/symfony/symfony/blob/7.1/src/Symfony/Bridge/Php= Unit/ClockMock.php > [6] https://gist.github.com/iluuu1994/4b83481baac563f8f0d3204c697c5551 >=20 =E2=80=94 Rob --db986c3ee94548d495ad1297aca362ca Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable

=
On Fri, Aug 2, 2024, at 18:51, Ilija Tovilo wrote:
Hi everyone

As you probably know, a common performance optim= ization in PHP is to
prefix global function calls in names= paced code with a `\`. In
namespaced code, relative functi= on calls (meaning, not prefixed with
`\`, not imported and= not containing multiple namespace components)
will be loo= ked up in the current namespace before falling back to the
global namespace. Prefixing the function name with `\` disambiguates
the called function by always picking the global function.

Not knowing exactly which function is called= at compile time has a
couple of downsides to this:

* It leads to the aforementioned double-lookup.
* It prevents compile-time-evaluation of pure internal func= tions.
* It prevents compiling to specialized  opcode= s for specialized
internal functions (e.g. strlen()).
<= /div>
* It requires branching for frameless functions [1].
=
* It prevents an optimization that looks up internal functions by
offset rather than by name [2].
* It prevents= compiling to more specialized argument sending opcodes
be= cause of unknown by-value/by-reference passing.

=
All of these are enabled by disambiguating the call. Unfortunately,=
prefixing all calls with `\`, or adding a `use function` = at the top of
every file is annoying and noisy. We recentl= y got a feature request to
change how functions are looked= up [3]. The approach that appears to
cause the smallest b= ackwards incompatibility is to flip the order in
which fun= ctions are looked up: Check in global scope first, and only
then in local scope. With this approach, if we can find a global
function at compile-time, we know this is the function that wil= l be
picked at run-time, hence automatically enabling the = optimizations
above. I created a PoC implementing this app= roach [4].

M=C3=A1t=C3=A9 has kindly benchm= arked the patch, measuring an improvement of
~3.9% for Lar= avel, and ~2.1% for Symfony
This = seems quite significant, given that no changes were required in
either of these two codebases.

So, what you=E2=80=99re saying is that symfony and laravel can ge= t a performance increase by simply adding a \ in the right places? Why d= on=E2=80=99t they do that instead of changing the language?


There are a few noteworthy downsides:

* Unqualified calls to functions in the same namespace would be
slightly slower, because they now involve checking global scope = first.
I believe that unqualified, global calls are much m= ore common, so this
change should still result in a net po= sitive. It's also possible to
avoid this cost by adding a = `use function` to the top of the file.

For functions/classes in the same exact namespace, you don=E2=80= =99t need a use statement. But after this change, you do in certain case= s?

namespace Foo;

<= div>function array_sum($bar) {}

function ba= z($bar) {
  return array_sum($bar);
}

So, how do you use that function in the same= file?

* Introducing new functions in the global namespace could cause a= BC
break for unqualified calls, if the function happens t= o have the same
name. This is unfortunate, but likely rare= . Since new functions are
only introduced in minor/major v= ersions, this should be manageable,
but must be considered= for every PHP upgrade.

We can= only see open source code when doing impact analysis. This means pickin= g even a slightly =E2=80=9Cpopular=E2=80=9D name could go very poorly.

*= Some mocking libraries (e.g. Symfony's ClockMock [5]) intentionally
=
declare functions called from some file in the files namespac= e to
intercept these calls. This use-case would break. Tha= t said, it is
somewhat of a fragile approach to begin with= , given that it wouldn't
work for fully qualified calls, o= r unnamespaced code.

See above= . I=E2=80=99ve seen this =E2=80=9Ctrick=E2=80=9D used on many closed sou= rce projects. I=E2=80=99ve also seen it used when PHP has a bug and the = workaround is to implement it in php like this. 


I p= erformed a small impact analysis [6]. There are 484 namespaced
=
functions shadowing global, internal functions in the top 1000
<= /div>
composer packages. However, the vast majority (464) of these f= unctions
come from thecodingmachine/safe, whose entire pur= pose is offering
safer wrappers around internal functions.= Excluding this library,
there are only 20 shadowing funct= ions, which is surprisingly little.
Furthermore, the patch= would have no impact on users of
thecodingmachine/safe, o= nly on the library code itself.

As for prov= iding a migration path: One approach might be to introduce
an INI setting that performs the function lookup in both local and
<= /div>
global scope at run-time, and informs the user about the behav= ioral
change in the future. To mitigate it, an explicit `u= se function` would
need to be added to the top of the file= , or the call would need to be
prefixed with `namespace\`.= The impact analysis [6] also provides a
script that looks= for shadowing functions in your project. It does not
iden= tify uses of these functions (yet), just their declarations.

Lastly, I've already raised this idea in the PHP Found= ations internal
chat but did not receive much positive fee= dback, mostly due to fear of
the potential BC impact. I'm = not particularly convinced this is an
issue, given the imp= act analysis. Given the surprisingly large
performance ben= efits, I was inclined to raise it here anyway. It also
spa= rked some related ideas, like providing modules that lock
= namespaces and optimize multiple files as a singular unit. That said,
such approaches would likely be significantly more complex t= han the
approach proposed here (~30 lines of C code).
<= /div>

Anyway, please let me know about possible conce= rns, broken use-cases,
or any alternative approaches that = may come to mind. I'm looking
forward to your feedback.

Ilija

[1] <= a href=3D"https://github.com/php/php-src/pull/12461">https://github.com/= php/php-src/pull/12461
[4] https:= //github.com/php/php-src/pull/14529


=E2=80= =94 Rob
--db986c3ee94548d495ad1297aca362ca--