GC improvement #3165
GC improvement #3165
Conversation
I will review and run benchmarks on this today in the evening. |
Here are my benchmark results, comparing three different workloads between master (OLD) and this PR (NEW). The first workload currently completely trashes the GC implementation on master, the other two are more lightweight. Here the GC "disabled" lines mean disabled at runtime, so roots are still collected.
The most interesting is probably the first result:
For the compression, I wonder if the current scheme that separates compressed & uncompressed addresses is really worthwhile. I would suggest to always mask the address (something like this: https://gist.github.com/nikic/515dc2dfdc4912cee5c6e1fb17a4d276). This means that we always have to do a In any case, I really like this implementation, dropping the linked lists makes this a lot nicer. It's a great improvement, let's land it! |
/* bit stealing tags for gc_root_buffer.ref */ | ||
#define GC_BITS 0x3 | ||
|
||
#define GC_ROOT 0x0 /* poissible root of circular garbage */ |
nikic
Mar 1, 2018
Member
nit: poissible -> possible
nit: poissible -> possible
} | ||
} | ||
if (GC_G(buf_size) < GC_BUF_GROW_STEP) { | ||
new_size = GC_G(buf_size) *= 2; |
nikic
Mar 1, 2018
Member
nit: Can be just *
instead of *=
, as GC_G(buf_size)
is assigned below.
nit: Can be just *
instead of *=
, as GC_G(buf_size)
is assigned below.
|
||
static void gc_adjust_threshold(int count) | ||
{ | ||
uint32_t new_threshold; |
nikic
Mar 1, 2018
Member
nit: Indentation
nit: Indentation
addr = GC_G(unused); | ||
root = GC_G(buf) + addr; | ||
ZEND_ASSERT(GC_IS_UNUSED(root->ref)); | ||
GC_G(unused) = (uint32_t)(uintptr_t)GC_GET_PTR(root->ref) / sizeof(void*); |
nikic
Mar 1, 2018
Member
The GC_GET_PTR here can be dropped. At least for me the mask is not optimized away.
The GC_GET_PTR here can be dropped. At least for me the mask is not optimized away.
return addr; | ||
} | ||
|
||
static zend_always_inline void gc_ref_set_info(zend_refcounted *ref, uint32_t info) |
nikic
Mar 1, 2018
Member
I think this function is no longer used.
I think this function is no longer used.
gc_root_buffer *newRoot; | ||
|
||
if (UNEXPECTED(CG(unclean_shutdown)) || UNEXPECTED(GC_G(gc_active))) { | ||
if (UNEXPECTED(GC_G(gc_protected)) || UNEXPECTED(CG(unclean_shutdown))) { |
nikic
Mar 1, 2018
Member
We could explicitly set gc_protected
on unclean shutdown and save one check here.
We could explicitly set gc_protected
on unclean shutdown and save one check here.
dstogov
Mar 1, 2018
Author
Member
Right. I already thought about this. Lets do this after merge.
Right. I already thought about this. Lets do this after merge.
gc_remove_nested_data_from_buffer(current->ref, current); | ||
n = GC_FIRST_REAL_ROOT; | ||
current = GC_G(buf) + GC_FIRST_REAL_ROOT; | ||
last = GC_G(buf) + GC_G(first_unused); |
nikic
Mar 1, 2018
Member
last
looks unused here.
last
looks unused here.
@nikic thank you for review and benchmarks. Really impressive :) You may play with GC_THRESHOLD adoption after the merge. I'll think about compression once again. I hope, I'll merge this by tomorrow evening. |
while (idx < GC_G(first_unused)) { | ||
gc_root_buffer *root = GC_G(buf) + idx; | ||
|
||
if (root->ref == ref) { |
nikic
Mar 2, 2018
•
Member
You're right, I missed the GC_GET_PTR()
. I guess we need it in this line as well, it's just less likely to hit here.
You're right, I missed the GC_GET_PTR()
. I guess we need it in this line as well, it's just less likely to hit here.
dstogov
Mar 2, 2018
Author
Member
I've found a compromise, that keep good performance an makes small overhead only for apps with big data sets - (GC_G(first_unused) >= GC_MAX_UNCOMPRESSED).
I've found a compromise, that keep good performance an makes small overhead only for apps with big data sets - (GC_G(first_unused) >= GC_MAX_UNCOMPRESSED).
Amazing work! |
These recent GC changes cause segfaults, see https://bugs.php.net/bug.php?id=76050. |
Thanks for catching. |
No description provided.