Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124718 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 6AB971A00B7 for ; Fri, 2 Aug 2024 16:51:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1722617605; bh=lAfnXoEjldWyb/Te5bQICEorlStAIxU9ri/4dXVJX20=; h=From:Date:Subject:To:From; b=LJjv/jlNTQZo3Fzu6TMGPq2hd/NTfIgzpBRFIaAX68qRIKox+hYnQHAdEX4uN1W3D CObD5vYq0odx/lEa9N2+g65XhAnmemanTT9pBWwPsQYshB1YG1vwlaG6TwmoBAsw+l 1YMHfKLAKR2RpBziqOHBoxW1dE/f5p6iLK7TEGXxovcTgqEmwpf1Z3NeerI05gxp51 LZHCjJzgJrCOEsSCIQ3+rClYLpqKym1X4NRvWYDwXTxrQwjVNlxJ3hadr4hvPqot+Z N1gjkfn5gUXrr+GO9dINdq5mGsrAh9OcayFSSNl/xB/5xveu+OujmbzU8OXKTVwLNE IYMjpGvlfNGWQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id BF8D518007E for ; Fri, 2 Aug 2024 16:53:24 +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.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-ot1-f41.google.com (mail-ot1-f41.google.com [209.85.210.41]) (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 16:53:24 +0000 (UTC) Received: by mail-ot1-f41.google.com with SMTP id 46e09a7af769-70949118d26so5367926a34.0 for ; Fri, 02 Aug 2024 09:51:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1722617504; x=1723222304; darn=lists.php.net; h=content-transfer-encoding:to:subject:message-id:date:from :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=6gtHBVEsvA46TCMuT8O/iNRY9OHp8wN47pk8L0vCRvw=; b=Q3p/s759YmTw4z6+y1lNN9rKGclNYoB470DBvidjwM2nMyg3Dpcwn2VBZFTW7nHmYc FhhIBfL8wOh7EoeOUnUDdAOfVPn00qu+t2rHUvZi76rHgFQ1KMZlKUsQadaV73ItKzx1 FhSXK67qVvvMKawydeVkstkPqIZTlDEBRGjCWNEhq78VaSYVYP6z4PviJbQZiw0XkzIo QgmhPZprRkKgk99SdWc3bm1sMK4qRiEQV1v1RDwYR3oc41gWWAs0lY8ydy32VPsPDALI e+L+zbXspARQLppiIVvOUGAECJL+TEXJF3mzydH9EjjhO6lIdKKGBjx0T9jykAibvCz2 m25Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722617504; x=1723222304; h=content-transfer-encoding:to:subject:message-id:date:from :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=6gtHBVEsvA46TCMuT8O/iNRY9OHp8wN47pk8L0vCRvw=; b=Qk44mDlc23A9L3vqXCkrAV4pHLIdgGaGnfl8eyjo3Lzb0bQimkvLFayCD5V8kC7JdG 4Y19PPPl5ZIyzo8EgEC/GypebHiCFRblKNOn5NnfGfZsIcrX5FoT1b9RgohzI1RbcB6f sszZpID7dpkvlrFEvXuFaHSrlk0N9hfQ4RESlVJjJ5OwvxvlH6VBSvCa1YbEmVXq52Xy xLQQbADjcpSKwCRUPE4S/KOn3FEpPkkBr4O4UJrCNf0quhTgA9LS1q5HLuhE04UYW/4k TNYEWsJ0dIDLv55oZpxNLMPGjunlaOHnPEXoI2LE1QBvAH6iH19xPUXiHOph7VIwb+9s 2jgQ== X-Gm-Message-State: AOJu0Yw629n3sAQdnZE7coN2wvJUDIWN7B6Uk9iqDhcTq6M3F/kw4Nb8 JKpHonxQ33x0T7ilMiDe3LZFK8on4YhRypdADlWNFYqckifrDAouC/9xJg3NcTv0wi1HhzQNttE uzjCzeZFStJ8z06D3igVnMJlqscExQ/1oBiC6fw== X-Google-Smtp-Source: AGHT+IHbRJ90INCXcBkm1vVGOJyvGJ6eoXDm50Nab4Pxd0jcRDoV7/AI6AxphLMaIVi40y9P1ZnN28Y1yQ55aERUTmE= X-Received: by 2002:a05:6358:54a2:b0:1ac:f4a9:45 with SMTP id e5c5f4694b2df-1af3b9fcae6mr484249155d.3.1722617503862; Fri, 02 Aug 2024 09:51:43 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Fri, 2 Aug 2024 18:51:33 +0200 Message-ID: Subject: [PHP-DEV] [Concept] Flip relative function lookup order (global, then local) To: PHP internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: tovilo.ilija@gmail.com (Ilija Tovilo) Hi everyone 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. 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 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. 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]. M=C3=A1t=C3=A9 has kindly benchmarked the patch, measuring an improvement o= f ~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. 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 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. * 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. * 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. 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. 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. 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). 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. Ilija [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/PhpUnit/= ClockMock.php [6] https://gist.github.com/iluuu1994/4b83481baac563f8f0d3204c697c5551