https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112014-03-07T09:01:58ZRuby Issue Tracking SystemRuby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=456722014-03-07T09:01:58Zko1 (Koichi Sasada)
<ul><li><strong>Description</strong> updated (<a title="View differences" href="/journals/45672/diff?detail_id=32982">diff</a>)</li></ul> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=456732014-03-07T09:05:57Zko1 (Koichi Sasada)
<ul><li><strong>Description</strong> updated (<a title="View differences" href="/journals/45673/diff?detail_id=32983">diff</a>)</li></ul> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=456742014-03-07T09:07:47Zko1 (Koichi Sasada)
<ul><li><strong>File</strong> <a href="/attachments/4282">gc.patch</a> <a class="icon-only icon-download" title="Download" href="/attachments/download/4282/gc.patch">gc.patch</a> added</li></ul><p>Patch is added.</p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=458292014-03-16T21:19:57Znormalperson (Eric Wong)normalperson@yhbt.net
<ul></ul><p><a href="mailto:ko1@atdot.net" class="email">ko1@atdot.net</a> wrote:</p>
<blockquote>
<p>File gc.patch added</p>
</blockquote>
<p>Running this (on top of current trunk) to serve my (mostly static sites)<br>
on yhbt.net. Memory usage seems stable at ~31M (from ~49M)</p>
<p>I noticed vm1_gc_short_with_complex_long got very slow with this<br>
patch: ~10s => ~118s<br>
I ran this several times to be sure. 2.0.0 only took around ~13s</p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=458342014-03-17T04:09:24Zko1 (Koichi Sasada)
<ul></ul><p>Eric Wong wrote:</p>
<blockquote>
<p>Running this (on top of current trunk) to serve my (mostly static sites)<br>
on yhbt.net. Memory usage seems stable at ~31M (from ~49M)</p>
</blockquote>
<p>Thanks!</p>
<blockquote>
<p>I noticed vm1_gc_short_with_complex_long got very slow with this<br>
patch: ~10s => ~118s<br>
I ran this several times to be sure. 2.0.0 only took around ~13s</p>
</blockquote>
<p>OMG. Thank you for reporting.</p>
<p>Yes. It will do useless marking (minor marking) because of there are no empty spaces.</p>
<p>mmm.</p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=458962014-03-21T21:58:36Znormalperson (Eric Wong)normalperson@yhbt.net
<ul></ul><p>Eric Wong <a href="mailto:normalperson@yhbt.net" class="email">normalperson@yhbt.net</a> wrote:</p>
<blockquote>
<p><a href="mailto:ko1@atdot.net" class="email">ko1@atdot.net</a> wrote:</p>
<blockquote>
<p>File gc.patch added</p>
</blockquote>
<p>Running this (on top of current trunk) to serve my (mostly static sites)<br>
on yhbt.net. Memory usage seems stable at ~31M (from ~49M)</p>
</blockquote>
<p>Actually, not, it hit ~89M(!). It could be I got a traffic surge (but I<br>
did not pay attention to that).</p>
<p>I also did not set MALLOC_MMAP_THRESHOLD_ for eglibc malloc;<br>
only MALLOC_ARENA_MAX=1 MALLOC_ARENA_CHECK=1. So this may be<br>
a problem of malloc fragmentation, too.</p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=461382014-04-10T04:05:07ZStudent (Nathan Zook)blogger@pierian-spring.net
<ul></ul><p>I wonder if it might not be better to give the user control? Specifically, consider a web application. It would make sense to hold off promoting objects created during a call-response cycle until the end of the cycle. But there is no way for the GC to know when that might be. This is trivial for the application to know.</p>
<p>More generally, applications know when they are entering periods with lots of mid-term objects being created. Adding GC.no_promote would allow an application to tune this behavior. I would suggest allowing calls with and without blocks, the blockless form would be reveresed by GC.allow_promote.</p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=461392014-04-10T05:38:37Znormalperson (Eric Wong)normalperson@yhbt.net
<ul></ul><p>GC.promote/allow_promote can work in some cases, but I consider it too<br>
ugly; as ugly as OobGC. It would also be error prone and hard to work<br>
in multithreaded situations.</p>
<p>I would rather have work towards automatic run-time optimizations<br>
(perhaps via online profiling/escape-analysis) than encourage users to<br>
do brittle/ugly things in their code for short-term benefit.</p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=471042014-06-09T11:37:46Zko1 (Koichi Sasada)
<ul><li><strong>File</strong> <a href="/attachments/4474">discourse_benchmark.png</a> <a class="icon-only icon-download" title="Download" href="/attachments/download/4474/discourse_benchmark.png">discourse_benchmark.png</a> added</li><li><strong>File</strong> <a href="/attachments/4475">young_objects.png</a> <a class="icon-only icon-download" title="Download" href="/attachments/download/4475/young_objects.png">young_objects.png</a> added</li></ul><p>After introducing AGE2_PROMOTION patch, only a few old objects are added for each minor GC.<br>
The impact of this issue (too many slots) are not critical now. But some application can not protect increasing total slots.</p>
<p>I rewrite algorithm more simple:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">gc</span>
<span class="k">if</span> <span class="n">do_major_gc_at_next_gc</span>
<span class="n">major_gc</span> <span class="o">=</span> <span class="kp">false</span>
<span class="n">minor_mark</span><span class="p">()</span>
<span class="k">else</span>
<span class="n">major_gc</span> <span class="o">=</span> <span class="kp">true</span>
<span class="n">major_mark</span><span class="p">()</span>
<span class="n">last_major_gc</span> <span class="o">=</span> <span class="no">GC</span><span class="p">.</span><span class="nf">count</span>
<span class="k">end</span>
<span class="n">sweep</span><span class="p">()</span> <span class="c1"># Actually it is lazy sweep.</span>
<span class="k">if</span> <span class="n">total_slots</span> <span class="o">*</span> <span class="mf">0.7</span> <span class="o"><</span> <span class="n">using_slots</span>
<span class="c1"># not enough space</span>
<span class="k">if</span> <span class="n">major_gc</span> <span class="o">||</span>
<span class="no">GC</span><span class="p">.</span><span class="nf">count</span> <span class="o">-</span> <span class="n">last_major_gc</span> <span class="o">></span> <span class="mi">2</span> <span class="c1"># (A) extend heap at least 2 minor GC run</span>
<span class="n">extend_heap</span>
<span class="k">else</span>
<span class="n">do_major_gc_at_next_gc</span> <span class="o">=</span> <span class="kp">true</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>This algorithm simply do:</p>
<p>(1) If not enough slots after minor GC, do major GC at next GC<br>
(2) If not enough slots after major GC, extend heap<br>
(3) If not enough slots after minor GC <em>and</em> only a few minor GC until last major GC (it should be not enough slots), extend heap ((A) in the above algorithm)</p>
<p>This strategy keeps lower total slots.</p>
<p><img src="https://bugs.ruby-lang.org/attachments/download/4474/discourse_benchmark.png" alt="discorse benchmark result" loading="lazy"></p>
<p>This picture shows the (a) total_slots (b) old_objects (c) ru_minflt (used memory pages given by getrusage) for current trunk and modified (above algorithm introduced) trunk using gc_tracer.</p>
<p>You can see:</p>
<p>(1) ru_minflt (rough memory usage) is linear to total_slots.<br>
(2) heap_slots/modified is fewer than heap_slots. It has impact.<br>
(3) old_objects/modified is reduced periodically. Do major GC aggressively compare with current trunk.<br>
(4) Modified trunk invokes higher number of GC events (similar to GC count). This is because total_slots number is fewer than current trunk. It is intentional.</p>
<p>You can increment consuming slot number using environment variables. So that I will introduce this patch. It means that default GC parameter is "low memory consuming/low performance" compare with current trunk/Ruby 2.1.</p>
<p>The patch is also small:</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">Index: gc.c
===================================================================
</span><span class="gd">--- gc.c (revision 46386)
</span><span class="gi">+++ gc.c (working copy)
</span><span class="p">@@ -531,6 +531,9 @@</span>
int parent_object_is_old;
int need_major_gc;
<span class="gi">+
+ size_t last_major_gc;
+
</span> size_t remembered_shady_object_count;
size_t remembered_shady_object_limit;
size_t old_object_count;
<span class="p">@@ -3035,15 +3038,13 @@</span>
(int)heap->total_slots, (int)heap_pages_swept_slots, (int)heap_pages_min_free_slots);
if (heap_pages_swept_slots < heap_pages_min_free_slots) {
<span class="gd">- heap_set_increment(objspace, heap_extend_pages(objspace));
- heap_increment(objspace, heap);
-
-#if USE_RGENGC
- if (objspace->rgengc.remembered_shady_object_count + objspace->rgengc.old_object_count > (heap_pages_length * HEAP_OBJ_LIMIT) / 2) {
- /* if [old]+[remembered shady] > [all object count]/2, then do major GC */
- objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_RESCAN;
</span><span class="gi">+ if (objspace->rgengc.during_minor_gc && objspace->profile.count - objspace->rgengc.last_major_gc > 2 /* magic number */) {
+ objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_NOFREE;
</span> }
<span class="gd">-#endif
</span><span class="gi">+ else {
+ heap_set_increment(objspace, heap_extend_pages(objspace));
+ heap_increment(objspace, heap);
+ }
</span> }
gc_prof_set_heap_info(objspace);
<span class="p">@@ -4256,6 +4257,7 @@</span>
objspace->profile.major_gc_count++;
objspace->rgengc.remembered_shady_object_count = 0;
objspace->rgengc.old_object_count = 0;
<span class="gi">+ objspace->rgengc.last_major_gc = objspace->profile.count;
</span> rgengc_mark_and_rememberset_clear(objspace, heap_eden);
}
#endif
</code></pre>
<p>PS. BTW, AGE2PROMOTION strategy introducing young objects, which is promoted from infant objects and will be promoted to old objects. I also counts them and I found that they are only a few objects in this program.</p>
<p><img src="https://bugs.ruby-lang.org/attachments/download/4475/young_objects.png" alt="young_objects" loading="lazy"></p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=471052014-06-09T11:43:30Zko1 (Koichi Sasada)
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Closed</i></li><li><strong>% Done</strong> changed from <i>0</i> to <i>100</i></li></ul><p>Applied in changeset r46387.</p>
<hr>
<ul>
<li>gc.c: change full GC timing to keep lower memory usage.<br>
Extend heap only at<br>
(1) after major GC<br>
or<br>
(2) after several (two times, at current) minor GC<br>
Details in <a href="https://bugs.ruby-lang.org/issues/9607#note-9" class="external">https://bugs.ruby-lang.org/issues/9607#note-9</a><br>
[Bug <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Change the full GC timing (Closed)" href="https://bugs.ruby-lang.org/issues/9607">#9607</a>]</li>
</ul> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=474622014-06-30T06:57:49Zko1 (Koichi Sasada)
<ul><li><strong>Backport</strong> changed from <i>1.9.3: UNKNOWN, 2.0.0: UNKNOWN, 2.1: UNKNOWN</i> to <i>1.9.3: UNKNOWN, 2.0.0: UNKNOWN, 2.1: REQUIRED</i></li></ul><p>The following patch is for current Ruby 2.1 branch.</p>
<p>Can anyone (who has memory consuming trouble) try this patch on Ruby 2.1 trunk?</p>
<p>I think this patch will decrease memory consumption on Ruby 2.1 (but increase major GC counts).</p>
<p>Chikanaga-san:<br>
Please consider this patch for next Ruby 2.1 release.</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">Index: gc.c
===================================================================
</span><span class="gd">--- gc.c (revision 46622)
</span><span class="gi">+++ gc.c (working copy)
</span><span class="p">@@ -519,6 +519,9 @@</span>
int parent_object_is_old;
int need_major_gc;
<span class="gi">+
+ size_t last_major_gc;
+
</span> size_t remembered_shady_object_count;
size_t remembered_shady_object_limit;
size_t old_object_count;
<span class="p">@@ -2954,14 +2957,17 @@</span>
(int)heap->total_slots, (int)heap_pages_swept_slots, (int)heap_pages_min_free_slots);
if (heap_pages_swept_slots < heap_pages_min_free_slots) {
<span class="gi">+#if USE_RGENGC
+ if (objspace->rgengc.during_minor_gc && objspace->profile.count - objspace->rgengc.last_major_gc > 2 /* magic number */) {
+ objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_NOFREE;
+ }
+ else {
+ heap_set_increment(objspace, (heap_pages_min_free_slots - heap_pages_swept_slots) / HEAP_OBJ_LIMIT);
+ heap_increment(objspace, heap);
+ }
+#else
</span> heap_set_increment(objspace, (heap_pages_min_free_slots - heap_pages_swept_slots) / HEAP_OBJ_LIMIT);
heap_increment(objspace, heap);
<span class="gd">-
-#if USE_RGENGC
- if (objspace->rgengc.remembered_shady_object_count + objspace->rgengc.old_object_count > (heap_pages_length * HEAP_OBJ_LIMIT) / 2) {
- /* if [old]+[remembered shady] > [all object count]/2, then do major GC */
- objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_RESCAN;
- }
</span> #endif
}
</code></pre> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=484342014-08-21T09:15:08Zko1 (Koichi Sasada)
<ul></ul><p>I checked benchmark results and no big regression are observed comapre with current edge of Ruby 2.1.<br>
<a href="http://www.atdot.net/sp/raw/ywfnan" class="external">http://www.atdot.net/sp/raw/ywfnan</a> (ruby_2_1a is same as ruby_2_1. to check accuracy (ideally, there should be no difference)).</p>
<p>I understand that the last patch doesn't work. Please try it:<br>
<a href="http://www.atdot.net/sp/raw/sabnan" class="external">http://www.atdot.net/sp/raw/sabnan</a></p>
<p>or</p>
<pre><code>Index: gc.c
===================================================================
--- gc.c (revision 47240)
+++ gc.c (working copy)
@@ -519,6 +519,9 @@
int parent_object_is_old;
int need_major_gc;
+
+ size_t last_major_gc;
+
size_t remembered_shady_object_count;
size_t remembered_shady_object_limit;
size_t old_object_count;
@@ -2954,14 +2957,17 @@
(int)heap->total_slots, (int)heap_pages_swept_slots, (int)heap_pages_min_free_slots);
if (heap_pages_swept_slots < heap_pages_min_free_slots) {
+#if USE_RGENGC
+ if (objspace->rgengc.during_minor_gc && objspace->profile.count - objspace->rgengc.last_major_gc > 2 /* magic number */) {
+ objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_NOFREE;
+ }
+ else {
+ heap_set_increment(objspace, (heap_pages_min_free_slots - heap_pages_swept_slots) / HEAP_OBJ_LIMIT);
+ heap_increment(objspace, heap);
+ }
+#else
heap_set_increment(objspace, (heap_pages_min_free_slots - heap_pages_swept_slots) / HEAP_OBJ_LIMIT);
heap_increment(objspace, heap);
-
-#if USE_RGENGC
- if (objspace->rgengc.remembered_shady_object_count + objspace->rgengc.old_object_count > (heap_pages_length * HEAP_OBJ_LIMIT) / 2) {
- /* if [old]+[remembered shady] > [all object count]/2, then do major GC */
- objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_RESCAN;
- }
#endif
}
</code></pre> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=484402014-08-21T21:58:45Znormalperson (Eric Wong)normalperson@yhbt.net
<ul></ul><p>Thanks, I can confirm good results on 2.1.2!</p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=485712014-08-30T16:32:12Znagachika (Tomoyuki Chikanaga)nagachika00@gmail.com
<ul><li><strong>Backport</strong> changed from <i>1.9.3: UNKNOWN, 2.0.0: UNKNOWN, 2.1: REQUIRED</i> to <i>1.9.3: UNKNOWN, 2.0.0: UNKNOWN, 2.1: DONE</i></li></ul><p>Thank you ko1 for providing a patch to <code>ruby_2_1</code>. I can apply it cleanly.<br>
Ant thank you eric for your confirmation.</p>
<p>Backported into <code>ruby_2_1</code> branch at r47326.</p> Ruby master - Bug #9607: Change the full GC timinghttps://bugs.ruby-lang.org/issues/9607?journal_id=486732014-09-05T05:17:32Zusa (Usaku NAKAMURA)usa@garbagecollect.jp
<ul><li><strong>Backport</strong> changed from <i>1.9.3: UNKNOWN, 2.0.0: UNKNOWN, 2.1: DONE</i> to <i>2.0.0: DONTNEED, 2.1: DONE</i></li></ul>