Skip to content

Conversation

@bwoebi
Copy link
Member

@bwoebi bwoebi commented May 5, 2016

Previously cases like

class A { function foo() { static $a = 0; print $a++; } }
(new A)->foo();
class B extends A {}
(new B)->foo();
(new A)->foo();
(new B)->foo();

exposed different behavior depending on whether class B was early bound (add a surrounding if (1) { } statement) or the first call to the foo method happens after binding.
The reference was only installed at the first BIND_STATIC opcode, causing different behavior depending on whether statics were duplicated before or after BIND_STATIC.

This patch fixes that by always creating a reference for statics in functions, right at compile time.
The semantics are now that each function (with the exception of trait functions, which are expected to be literally copy-pasted) shares its statics with all instances of it.

The case above will now print 0123 in every case instead of 0011 or 0112 depending on binding times.

Also with Closures:

function getClosure() {
    return function () { static $a = 0; print $a++; };
}
$f = getClosure();
$f();
$g = getClosure();
$f();
$g();
$f();

will now print 0123 instead of 0101.

This makes behavior much more consistent.

To achieve this, it was needed to move to a zval+name array (instead of HashTable) for statics (as we cannot have mutable references in an immutable array in opcache).
Hence statics need to be copied directly into process memory upon each request instead of residing in shared memory. (That also removes the need for some run-time duplications.)
To support that, the patch adds a way to relocate pointers inside copied memory and arena allocates the copied parts.

Additionally, the better locality (statics now are typically closer to the op_array - side effect of arena allocation), one less fetch inside ZEND_BIND_STATIC (hello stalling cycles waiting for memory...) and much faster Closure binding account for (up to) 0.3% run-time improvement on real world applications (typical modern applications are relatively heavy on Closures nowadays).

@bwoebi bwoebi changed the title Make static binding consistent and faster Make static binding consistent and a little faster May 5, 2016
static_variables = func->static_variables;
func->static_variables = NULL;
zend_hash_str_del(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME)-1);
func->static_variables = static_variables;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, why change this one? the double free won't be occurred ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was fine in an earlier iteration of the patch … but now … Not quite (free is after refcount), but it will destroy the contents of the static vars ... Fixing and thanks for catching.

Previously cases like
class A { function foo() { static $a = 0; print $a++; } } (new A)->foo(); class B extends A {} (new B)->foo(); (new A)->foo(); (new B)->foo();
exposed different behavior depending on whether class B was early bound (add a surrounding if (1) { } statement) or the first call to the foo method happens after binding.
The reference was only installed at the first BIND_STATIC opcode, causing different behavior depending on whether statics were duplicated before or after BIND_STATIC.

This patch fixes that by always creating a reference for statics in functions, right at compile time.
The semantics are now that each function (with the exception of trait functions, which are expected to be literally copy-pasted) shares its statics with all instances of it.

The case above will now print 0123 in every case instead of 0011 or 0123 depending on binding times.

Also with Closures:
function getClosure() { return function () { static $a = 0; print $a++; }; } $f = getClosure(); $f(); $g = getClosure(); $f(); $g(); $f();
will now print 0123 instead of 0102.

This makes behavior much more consistent.

To achieve this, it was needed to move to a zval+name array (instead of HashTable) for statics (as we cannot have mutable references in an immutable array in opcache).
Hence statics need to be copied directly into process memory upon each request instead of residing in shared memory. (That also removes the need for some run-time duplications.)
To support that, the patch adds a way to relocate pointers inside copied memory and arena allocates the copied parts.

Additionally, the better locality (statics now are typically closer to the op_array - side effect of arena allocation), one less fetch inside ZEND_BIND_STATIC (hello stalling cycles waiting for memory...) and much faster Closure binding account for (up to) 0.3% run-time improvement on real world applications (typical modern applications are relatively heavy on Closures nowadays).
@krakjoe
Copy link
Member

krakjoe commented Jun 8, 2016

What is the status of this PR ?

Looks to me like a bug fix ...

@dstogov @bwoebi @nikic

@dstogov
Copy link
Member

dstogov commented Jun 8, 2016

Even, if this was a bug fix, it changes well-known PHP behavior.

I believe, this tricky "static property cloning" is used by some frameworks.

Thanks. Dmitry.


From: Joe Watkins notifications@github.com
Sent: Wednesday, June 8, 2016 8:46:07 AM
To: php/php-src
Cc: Dmitry Stogov; Mention
Subject: Re: [php/php-src] Make static binding consistent and a little faster (#1899)

What is the status of this PR ?

Looks to me like a bug fix ...

@dstogovhttps://github.com/dstogov @bwoebihttps://github.com/bwoebi @nikichttps://github.com/nikic

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://github.com//pull/1899#issuecomment-224494066, or mute the threadhttps://github.com/notifications/unsubscribe/ACZM0i5478ogvSEMY_9uZYNbw279GFjkks5qJlcfgaJpZM4IYLOy.

@krakjoe
Copy link
Member

krakjoe commented Jun 8, 2016

it changes well-known PHP behaviour.

Yes, I'm just trying to determine if it's going to be targeting 7.1.

@bwoebi
Copy link
Member Author

bwoebi commented Jun 8, 2016

Thanks for asking… I indeed forgot about this patch…

@dstogov Can you please figure out the actual BC impact?

@dstogov
Copy link
Member

dstogov commented Jun 8, 2016

It's clear. Now static properties in children classes are not shared with parent.

With the patch they are going to be common and modification of a static property in a single child will also affect all other children.


From: Bob Weinand notifications@github.com
Sent: Wednesday, June 8, 2016 2:32:26 PM
To: php/php-src
Cc: Dmitry Stogov; Mention
Subject: Re: [php/php-src] Make static binding consistent and a little faster (#1899)

Thanks for asking... I indeed forgot about this patch...

@dstogovhttps://github.com/dstogov Can you please figure out the actual BC impact?

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://github.com//pull/1899#issuecomment-224562743, or mute the threadhttps://github.com/notifications/unsubscribe/ACZM0nvYwqk85YYYfOcLEPFfT_9akw0oks5qJqhKgaJpZM4IYLOy.

@bwoebi
Copy link
Member Author

bwoebi commented Jun 8, 2016

That's the change, yes. The question was whether that has actual larger BC impact on actual applications?

@nikic
Copy link
Member

nikic commented Jun 8, 2016

@bwoebi Whether or not the BC break is large, this is a non-trivial change, and as such would probably benefit from an RFC.

@nikic nikic added the RFC label Jul 13, 2016
@nikic
Copy link
Member

nikic commented Feb 3, 2017

In OTR discussion (thread "Little faster and more consistent static binding" starting May 5, 2016) the reception to this change was negative. Do you still plan to pursue this, or can we close this PR?

@nikic
Copy link
Member

nikic commented May 1, 2017

As there doesn't seem to be further interest here, I'm closing this.

@nikic nikic closed this May 1, 2017
@dstogov
Copy link
Member

dstogov commented May 2, 2017 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants