Does PHP ever stack allocate?

php.internals

Olle Härstedt

5 years ago
Or is everything reference counted with heap allocation? Since PHP has escape analysis, this could be used to use the stack instead, and kill the memory when the scope ends. If PHP uses the stack, can this be seen in the opcode? This stackoverflow thread does not really answer my question. https://stackoverflow.com/questions/24223249/stack-and-heap-in-php#:~:text=In%20static%20typed%20languages%20all,known%20until%20the%20run%20time . Olle

Sara Golemon

5 years ago
On Fri, Jan 1, 2021 at 3:18 PM Olle Härstedt <olleharstedt@gmail.com> wrote:
> Or is everything reference counted with heap allocation? Since PHP has > escape analysis, this could be used to use the stack instead, and kill the > memory when the scope ends. If PHP uses the stack, can this be seen in the > opcode? > >
Well, you're not going to like this answer, but.... yes and no. A single PHP call frame holds a block of storage space for (among other things) all* local variables. This can be thought of analogously to "the stack" as it's used by native applications. Basic scalars (null, bool, int, float) sit in this space with no additional pointers to anywhere. Non-scalars use pointers to elsewhere in the heap to store the actual payload. This isn't unique to PHP, as these structures have runtime determined size and thus can't** be stack allocated. There's further asterii below all of those statements, but that's the high-level generalized answer to your question as posed. The implied question you asked is actually handled using another mechanism. PHP's internals have two separate memory allocation pools. "Persistent" memory allocation, which creates blocks of memory for the lifetime of the process, and "Engine" memory allocation, which are bulk de-allocated*** at the end of every request (after relevant destructors have fired). -Sara * All variables which are explicitly references as $foo (excluding auto-globals). ${'bar'} and $$baz style references to locals are special and require their own separate conversation. ** C's alloca() and similar techniques in other languages can reserve dynamic amounts of stack space, but let's ignore that power-move for the sake of this argument. *** Deallocated from the request handler's point of view, though the runtime's memory manager (usually) doesn't give it back to the OS immediately, since it's likely to be used by the next request anyway.

Olle Härstedt

5 years ago
2021-01-03 16:15 GMT, Sara Golemon <pollita@php.net>:
> On Fri, Jan 1, 2021 at 3:18 PM Olle Härstedt <olleharstedt@gmail.com> > wrote: > >> Or is everything reference counted with heap allocation? Since PHP has >> escape analysis, this could be used to use the stack instead, and kill >> the >> memory when the scope ends. If PHP uses the stack, can this be seen in >> the >> opcode? >> >> > Well, you're not going to like this answer, but.... yes and no. > > A single PHP call frame holds a block of storage space for (among other > things) all* local variables. This can be thought of analogously to "the > stack" as it's used by native applications. Basic scalars (null, bool, > int, float) sit in this space with no additional pointers to anywhere. > Non-scalars use pointers to elsewhere in the heap to store the actual > payload. This isn't unique to PHP, as these structures have runtime > determined size and thus can't** be stack allocated. > > There's further asterii below all of those statements, but that's the > high-level generalized answer to your question as posed. > > The implied question you asked is actually handled using another > mechanism. PHP's internals have two separate memory allocation pools. > "Persistent" memory allocation, which creates blocks of memory for the > lifetime of the process, and "Engine" memory allocation, which are bulk > de-allocated*** at the end of every request (after relevant > destructors have fired). > > -Sara > > * All variables which are explicitly references as $foo (excluding > auto-globals). ${'bar'} and $$baz style references to locals are special > and require their own separate conversation. > ** C's alloca() and similar techniques in other languages can reserve > dynamic amounts of stack space, but let's ignore that power-move for the > sake of this argument. > *** Deallocated from the request handler's point of view, though the > runtime's memory manager (usually) doesn't give it back to the OS > immediately, since it's likely to be used by the next request anyway. >
Thanks Sara! I realize I should have been more precise: Can PHP allocate non-reference counted memory that automatically is freed when leaving scope, similar to what Go does with escape analysis? Article describing the Go mechanism: https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/ Olle

tyson andre

5 years ago
Hi Olle, > Thanks Sara! I realize I should have been more precise: Can PHP > allocate non-reference counted memory that automatically is freed when > leaving scope, similar to what Go does with escape analysis? > > Article describing the Go mechanism: > https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/ Could you give some concrete examples of what type of code you're talking about? As Sara Golemon said, scalars (null, bool, int, float) are allocated on a php call frame, and the call frames go on a stack. That stack is separate from the C stack, but still a stack The call frame is "freed" when leaving scope - i.e. that part of the stack will be reused on subsequent calls. > A single PHP call frame holds a block of storage space for (among other > things) all* local variables. This can be thought of analogously to "the > stack" as it's used by native applications. Basic scalars (null, bool, > int, float) sit in this space with no additional pointers to anywhere. > Non-scalars use pointers to elsewhere in the heap to store the actual > payload. This isn't unique to PHP, as these structures have runtime > determined size and thus can't** be stack allocated. https://nikic.github.io/2017/04/14/PHP-7-Virtual-machine.html may help if you want to learn more about what the PHP VM currently does > So what’s the difference between TMP and VAR? Not much. > The distinction was inherited from PHP 5, where TMPs were VM stack allocated, > while VARs were heap allocated. In PHP 7 all variables are stack allocated. > As such, nowadays the main difference between TMPs and VARs is that only the latter are allowed to contain REFERENCEs > (this allows us to elide DEREFs on TMPs). Furthermore VARs may hold two types of special values, > namely class entries and INDIRECT values. The latter are used to handle non-trivial assignments. -Tyson

Olle Härstedt

5 years ago
2021-01-03 17:43 GMT, tyson andre <tysonandre775@hotmail.com>:
> Hi Olle, > >> Thanks Sara! I realize I should have been more precise: Can PHP >> allocate non-reference counted memory that automatically is freed when >> leaving scope, similar to what Go does with escape analysis? >> >> Article describing the Go mechanism: >> https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/ > > Could you give some concrete examples of what type of code you're talking > about? > As Sara Golemon said, scalars (null, bool, int, float) are allocated on a > php call frame, > and the call frames go on a stack. That stack is separate from the C stack, > but still a stack > > The call frame is "freed" when leaving scope - i.e. that part of the stack > will be reused on subsequent calls.
Sure. Consider the following trivial snippet: ```php class Point { public $x; public $y; } function foo() { $p = new Point(); return $p->x + $p->y; } ``` Since we know the lifetime of $p, we don't have to ref count it. Escape analysis helps with checking lifetimes and to remove needles ref counting (and heap allocations in compiled languages). There is a file in php-src for this: https://github.com/php/php-src/blob/master/ext/opcache/Optimizer/escape_analysis.c My question was about how this functionality is used.
>> A single PHP call frame holds a block of storage space for (among other >> things) all* local variables. This can be thought of analogously to "the >> stack" as it's used by native applications. Basic scalars (null, bool, >> int, float) sit in this space with no additional pointers to anywhere. >> Non-scalars use pointers to elsewhere in the heap to store the actual >> payload. This isn't unique to PHP, as these structures have runtime >> determined size and thus can't** be stack allocated. > > https://nikic.github.io/2017/04/14/PHP-7-Virtual-machine.html may help if > you want to learn more about what the PHP VM currently does
Nice link, thanks! Olle

Nikita Popov

5 years ago
On Mon, Jan 4, 2021 at 11:53 AM Olle Härstedt <olleharstedt@gmail.com> wrote:
> 2021-01-03 17:43 GMT, tyson andre <tysonandre775@hotmail.com>: > > Hi Olle, > > > >> Thanks Sara! I realize I should have been more precise: Can PHP > >> allocate non-reference counted memory that automatically is freed when > >> leaving scope, similar to what Go does with escape analysis? > >> > >> Article describing the Go mechanism: > >> > https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/ > > > > Could you give some concrete examples of what type of code you're talking > > about? > > As Sara Golemon said, scalars (null, bool, int, float) are allocated on a > > php call frame, > > and the call frames go on a stack. That stack is separate from the C > stack, > > but still a stack > > > > The call frame is "freed" when leaving scope - i.e. that part of the > stack > > will be reused on subsequent calls. > > Sure. Consider the following trivial snippet: > > ```php > class Point { > public $x; > public $y; > } > function foo() { > $p = new Point(); > return $p->x + $p->y; > } > ``` > > Since we know the lifetime of $p, we don't have to ref count it. > Escape analysis helps with checking lifetimes and to remove needles > ref counting (and heap allocations in compiled languages). There is a > file in php-src for this: > > https://github.com/php/php-src/blob/master/ext/opcache/Optimizer/escape_analysis.c > > My question was about how this functionality is used. >
I believe the escape analysis is currently only used as part of SCCP to perform constant folding on objects. For example, we would be able to fold the following function down to "return 3;": function foo() { $p = new Point(); $p->x = 1; $p->y = 2; return $p->x + $p->y; } However, I think the optimization you have in mind is more along the lines of dropping the object and storing its properties as local variables instead. I don't think the escape analysis is particularly useful outside of stdClass possibly, as it will bail on classes from different files, and classes with constructors anyway. Nikita

Olle Härstedt

5 years ago
2021-01-05 10:51 GMT, Nikita Popov <nikita.ppv@gmail.com>:
> On Mon, Jan 4, 2021 at 11:53 AM Olle Härstedt <olleharstedt@gmail.com> > wrote: > >> 2021-01-03 17:43 GMT, tyson andre <tysonandre775@hotmail.com>: >> > Hi Olle, >> > >> >> Thanks Sara! I realize I should have been more precise: Can PHP >> >> allocate non-reference counted memory that automatically is freed when >> >> leaving scope, similar to what Go does with escape analysis? >> >> >> >> Article describing the Go mechanism: >> >> >> https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/ >> > >> > Could you give some concrete examples of what type of code you're >> > talking >> > about? >> > As Sara Golemon said, scalars (null, bool, int, float) are allocated on >> > a >> > php call frame, >> > and the call frames go on a stack. That stack is separate from the C >> stack, >> > but still a stack >> > >> > The call frame is "freed" when leaving scope - i.e. that part of the >> stack >> > will be reused on subsequent calls. >> >> Sure. Consider the following trivial snippet: >> >> ```php >> class Point { >> public $x; >> public $y; >> } >> function foo() { >> $p = new Point(); >> return $p->x + $p->y; >> } >> ``` >> >> Since we know the lifetime of $p, we don't have to ref count it. >> Escape analysis helps with checking lifetimes and to remove needles >> ref counting (and heap allocations in compiled languages). There is a >> file in php-src for this: >> >> https://github.com/php/php-src/blob/master/ext/opcache/Optimizer/escape_analysis.c >> >> My question was about how this functionality is used. >> > > I believe the escape analysis is currently only used as part of SCCP to > perform constant folding on objects. For example, we would be able to fold > the following function down to "return 3;": > > function foo() { > $p = new Point(); > $p->x = 1; > $p->y = 2; > return $p->x + $p->y; > } > > However, I think the optimization you have in mind is more along the lines > of dropping the object and storing its properties as local variables > instead.
Hm, this is called scalar replacement, I think. Olle

Sara Golemon

5 years ago
On Sun, Jan 3, 2021 at 10:37 AM Olle Härstedt <olleharstedt@gmail.com> wrote:
> Thanks Sara! I realize I should have been more precise: Can PHP > allocate non-reference counted memory that automatically is freed when > leaving scope, similar to what Go does with escape analysis? >
It seems like you're conflating userspace variables and their underlying data storage mechanisms and while the two are related and have naturally similar semantics, they're not really the same thing and trying to generically talk about both at the same time is only going to add to confusion. If you're talking about userspace variables... Yeah. They're scope bound and destructed/freed on scope exit. Nearly every language does some form of this because to not do so would be leaky and awful. If you're talking about internal memory allocators, then the idea of "automatically freed" is a red-herring. It's an illusion created by some languages to cover up the quite explicit mechanisms in place to handle data lifetimes. PHP does its form of this as well using a combination of stack and heap allocators as appropriate. -Sara