Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127269 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 lists.php.net (Postfix) with ESMTPS id 6FF561A00BC for ; Fri, 2 May 2025 08:52:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1746175832; bh=fe9TXzcvuT3ztJx81Dc/61g+6gsomHRlthBD60yrVBA=; h=Date:From:To:In-Reply-To:References:Subject:From; b=K6duaSma1ue//8JGrhuO0UZUuvePG5Wf0tAwdsfGYTHP5MNjQ9U5a3ob6tQ1i06nH O9rIqx+mAJ9OZsVkgjTzNdz8nnTzcUYwBbhcrYEMnTlmC1oxF1ZMnLd+btuuD/VWY9 oDhjNjQSdziq25oUqMw1M7/nyLFSekIkXpnV7TAanW4vCgdHbrdUFIoksAdag99fhw dS7peGb4J7tBohVd/ViwiltFp5J1jotsl6wX3MYQfON3Ljmwfc/jQ9Pg4hJ0Zhgryr RsruGRKUm1i9Z6aghqCKjOlCD4/3e+ZsPk/UaCaYixacXWx1ncUz6zkhCqoVsEPJiR q5uOMkSjdLuvw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id F3E9A180034 for ; Fri, 2 May 2025 08:50:30 +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=-2.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from fout-b4-smtp.messagingengine.com (fout-b4-smtp.messagingengine.com [202.12.124.147]) (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 May 2025 08:50:30 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.phl.internal [10.202.2.45]) by mailfout.stl.internal (Postfix) with ESMTP id A622B114008D for ; Fri, 2 May 2025 04:52:45 -0400 (EDT) Received: from phl-imap-09 ([10.202.2.99]) by phl-compute-05.internal (MEProxy); Fri, 02 May 2025 04:52:45 -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=1746175965; x=1746262365; bh=fe9TXzcvuT 3ztJx81Dc/61g+6gsomHRlthBD60yrVBA=; b=hJ2FZoO5mWouxK0DB5G6oQbci+ QmfvdUH0Z2IlsH+Xa3h+KhenP9sUhyjduoWarAWB0THsNnosoyOV2/YDjP9AoUwN 1OD/gf1U82luy47uzbMKmzrkqAnPHoKGgVeNmSdIlK5RAkfQXL7fEgRHqMTMP8zN vzE/f4vJSkc4q/7PMDNxz4QjbS8bmV0A5AyqJkQBRChZVs9RIrsECujMlc198wAs ptkpzcK/k04oQE3odE2BMLnV/KRviNCbdm8F5gtaVOU8zx3jpeSE6ql60NY+dIIe /iv7Ps/JBq7wg5RllXNkOdEOVTLm9+gRw7Ko7zmqeG1eYngbnSLqMWv0YmhA== 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-sender:x-me-sender:x-sasl-enc; s=fm3; t= 1746175965; x=1746262365; bh=fe9TXzcvuT3ztJx81Dc/61g+6gsomHRlthB D60yrVBA=; b=Fpu3ChrOjRBtv8TLEbgPYNk+s1bNfcNnp9p7CVdvYkErwuAnm3J f70cMXyE9p3JtlV7o3EuZbesQH6C6gyerKopuqlUDXBxK77jZjurF+T9OPygE50I CXGd37V4ow2rBbX08J22JbvI7+BP/qrJvwZKamMR3y5RXxMJ/QjIoNV2KJNExRip +LH+qEczg9jc3ZYNXzZj+SAi6wqBWLQcMpPjxHruealbbW8ymle+wiGWU0F3xISg QFVfWe3EbDA2eHhOIHkkzRpXqWSqskxxQlmBB0W7AME3LqwyPEsWfisTV0o2MEYQ GN9lhe5eHAB/XPWuiwuW2mjMNOUdEUePh3w== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvjedvtdduucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefogg ffhffvkfgjfhfutgesrgdtreerredtjeenucfhrhhomhepfdftohgsucfnrghnuggvrhhs fdcuoehrohgssegsohhtthhlvggurdgtohguvghsqeenucggtffrrghtthgvrhhnpedtue ejtdethfeulefhtdelieduteelffdtudelheffgedtieehhfelieejgfevgeenucevlhhu shhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehrohgssegsohhtth hlvggurdgtohguvghspdhnsggprhgtphhtthhopedupdhmohguvgepshhmthhpohhuthdp rhgtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id EB4FC78006A; Fri, 2 May 2025 04:52:44 -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 X-ThreadId: T3fe4db1101f1f707 Date: Fri, 02 May 2025 10:52:22 +0200 To: internals@lists.php.net Message-ID: In-Reply-To: References: Subject: Re: [PHP-DEV] Deferred backtrace generation algorithm. Content-Type: multipart/alternative; boundary=22bc37cfe8104cec9ff05027ab6cd06e From: rob@bottled.codes ("Rob Landers") --22bc37cfe8104cec9ff05027ab6cd06e Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Fri, May 2, 2025, at 10:14, Edmond Dantes wrote: > Good day, everyone. >=20 > In the neighboring thread Concept: Lightweight error channels, the > topic of exception performance was discussed. >=20 > The main issue is the immediate backtrace generation in the > constructor, which can cause up to a 50% performance loss compared to > return (as I understood from the discussion =E2=80=94 I may be mistake= n). >=20 > I have a small idea on how to improve the situation in cases where > exceptions are caught and a backtrace isn't needed. >=20 > Let=E2=80=99s assume we delay backtrace generation. In PHP, you can=E2= =80=99t just > keep a reference to a stack frame, since it may be destroyed. You > could copy it, of course (which is relatively inexpensive compared to > generating the full backtrace). >=20 > Based on that, there are two possible implementations: >=20 > * Generate the backtrace at the moment the stack is freed >=20 > * Clone the stack frame when the stack is freed (this is roughly what > happens in Python) >=20 > This would require changes to the functions > `zend_vm_stack_free_extra_args_ex` and > `zend_vm_stack_free_extra_args`. >=20 > These functions could be used to either clone the stack frame or > generate the full/partial backtrace at the right moment. >=20 > The information about whether something is referencing a stack frame > could be stored in a separate structure in `EG`, so there would be no > need to modify `zend_execute_data`. > Any code referencing the frame would, of course, need to correctly > decrement the reference counter. >=20 > For example, when an exception is created, it increments the reference > count on the current call frame, which guarantees that the frame stays > available until either the exception is destroyed or the backtrace is > generated. >=20 > Nuances: >=20 > * It=E2=80=99s more efficient to copy only the parts of the stack fram= e needed > for the backtrace, not the entire frame. This also applies to > parameter slots =E2=80=94 they should be converted immediately if the > DEBUG_BACKTRACE_IGNORE_ARGS flag is not set. >=20 > * When cloning the stack frame, the reference to the previous frame > should automatically be incremented. >=20 > At first glance, this algorithm doesn=E2=80=99t break backward compati= bility > in any way and spreads out memory/CPU costs. >=20 > Even if multiple exceptions are created within a function but no > backtrace is generated, overall memory usage decreases, since Zend > only retains the data structures currently needed, rather than > duplicating them in each backtrace. >=20 > Cloned call frames exist only once and can be shared across multiple e= xceptions. >=20 > **What are the benefits?** >=20 > * The cost of throwing an exception that is caught and not used at the > top level is nearly equivalent to a return operation. >=20 > * Lower memory usage when no backtrace is generated. >=20 > * It becomes possible to reuse a single backtrace generation for > multiple exceptions that share common frames (needs further > consideration). >=20 > **Drawbacks:** >=20 > * Exiting a PHP function consumes CPU to copy ~100 bytes of memory. >=20 > * Increased code complexity in the exception/stack frame modules. >=20 > These conclusions were made mentally without writing actual code. > But perhaps someone will have something to add =E2=80=94 either in fav= or or against. >=20 > --- > Ed. >=20 Hey Ed, > The main issue is the immediate backtrace generation in the > constructor, which can cause up to a 50% performance loss compared to > return (as I understood from the discussion =E2=80=94 I may be mistake= n). For what it is worth, the stack trace is not generated in the constructo= r (at least from the perspective of the developer). Using reflection to = create the exception without calling the constructor still results in th= e stack trace being generated in the exception object. If it were create= d only in the constructor, then I suspect this could all be resolved in = user-land libraries. =E2=80=94 Rob --22bc37cfe8104cec9ff05027ab6cd06e Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
On Fri, May = 2, 2025, at 10:14, Edmond Dantes wrote:
Good day, everyone.

In= the neighboring thread Concept: Lightweight error channels, the
topic of exception performance was discussed.

The main issue is the immediate backtrace generation in the
= constructor, which can cause up to a 50% performance loss compared to
return (as I understood from the discussion =E2=80=94 I may be m= istaken).

I have a small idea on how to improve= the situation in cases where
exceptions are caught and a back= trace isn't needed.

Let=E2=80=99s assume we del= ay backtrace generation. In PHP, you can=E2=80=99t just
keep a= reference to a stack frame, since it may be destroyed. You
co= uld copy it, of course (which is relatively inexpensive compared to
generating the full backtrace).

Based on = that, there are two possible implementations:

*= Generate the backtrace at the moment the stack is freed

<= /div>
* Clone the stack frame when the stack is freed (this is rough= ly what
happens in Python)

This would= require changes to the functions
`zend_vm_stack_free_extra_ar= gs_ex` and
`zend_vm_stack_free_extra_args`.

These functions could be used to either clone the stack frame or<= /div>
generate the full/partial backtrace at the right moment.
=

The information about whether something is referenci= ng a stack frame
could be stored in a separate structure in `E= G`, so there would be no
need to modify `zend_execute_data`.
Any code referencing the frame would, of course, need to correc= tly
decrement the reference counter.

= For example, when an exception is created, it increments the reference
count on the current call frame, which guarantees that the fram= e stays
available until either the exception is destroyed or t= he backtrace is
generated.

Nuances:

* It=E2=80=99s more efficient to copy only the p= arts of the stack frame needed
for the backtrace, not the enti= re frame. This also applies to
parameter slots =E2=80=94 they = should be converted immediately if the
DEBUG_BACKTRACE_IGNORE_= ARGS flag is not set.

* When cloning the stack = frame, the reference to the previous frame
should automaticall= y be incremented.

At first glance, this algorit= hm doesn=E2=80=99t break backward compatibility
in any way and= spreads out memory/CPU costs.

Even if multiple= exceptions are created within a function but no
backtrace is = generated, overall memory usage decreases, since Zend
only ret= ains the data structures currently needed, rather than
duplica= ting them in each backtrace.

Cloned call frames= exist only once and can be shared across multiple exceptions.

**What are the benefits?**

* Th= e cost of throwing an exception that is caught and not used at the
=
top level is nearly equivalent to a return operation.
* Lower memory usage when no backtrace is generated.

* It becomes possible to reuse a single backtrace gene= ration for
multiple exceptions that share common frames (needs= further
consideration).

**Drawbacks:= **

* Exiting a PHP function consumes CPU to cop= y ~100 bytes of memory.

* Increased code comple= xity in the exception/stack frame modules.

Thes= e conclusions were made mentally without writing actual code.
= But perhaps someone will have something to add =E2=80=94 either in favor= or against.

---
Ed.


Hey Ed,

The main issue is the immed= iate backtrace generation in the
constructor, which can cause = up to a 50% performance loss compared to
return (as I understo= od from the discussion =E2=80=94 I may be mistaken).
<= div>
For what it is worth, the stack trace is not generate= d in the constructor (at least from the perspective of the developer). U= sing reflection to create the exception without calling the constructor = still results in the stack trace being generated in the exception object= . If it were created only in the constructor, then I suspect this could = all be resolved in user-land libraries.

=E2=80=94 Rob
--22bc37cfe8104cec9ff05027ab6cd06e--