[Feature #18885]

For now, the optimizations performed are:

  - Run a major GC
  - Compact the heap
  - Promote all surviving objects to oldgen

Other optimizations may follow.
This commit is contained in:
Jean Boussier 2022-10-19 16:56:37 +02:00 коммит произвёл Jean Boussier
Родитель a84c99468f
Коммит ba6ccd8714
7 изменённых файлов: 94 добавлений и 0 удалений

Просмотреть файл

@ -35,6 +35,13 @@ Note: We're only listing outstanding class updates.
The class use equality semantic to lookup keys like a regular hash,
but it doesn't hold strong references on the keys. [[Feature #18498]]
* Process.warnup
* Notify the Ruby virtual machine that the boot sequence is finished,
and that now is a good time to optimize the application. This is useful
for long running applications. The actual optimizations performed are entirely
implementation specific and may change in the future without notice. [[Feature #18885]
## Stdlib updates
The following default gems are updated.

Просмотреть файл

@ -11024,7 +11024,9 @@ process.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
process.$(OBJEXT): {$(VPATH)}thread_native.h
process.$(OBJEXT): {$(VPATH)}util.h
process.$(OBJEXT): {$(VPATH)}vm_core.h
process.$(OBJEXT): {$(VPATH)}vm_debug.h
process.$(OBJEXT): {$(VPATH)}vm_opts.h
process.$(OBJEXT): {$(VPATH)}vm_sync.h
ractor.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
ractor.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
ractor.$(OBJEXT): $(CCAN_DIR)/list/list.h

31
gc.c
Просмотреть файл

@ -9833,6 +9833,26 @@ garbage_collect_with_gvl(rb_objspace_t *objspace, unsigned int reason)
}
}
static int
gc_promote_object_i(void *vstart, void *vend, size_t stride, void *data)
{
rb_objspace_t *objspace = &rb_objspace;
VALUE v = (VALUE)vstart;
for (; v != (VALUE)vend; v += stride) {
switch (BUILTIN_TYPE(v)) {
case T_NONE:
case T_ZOMBIE:
break;
default:
if (!RVALUE_OLD_P(v) && !RVALUE_WB_UNPROTECTED(v)) {
RVALUE_AGE_SET_OLD(objspace, v);
}
}
}
return 0;
}
static VALUE
gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE immediate_mark, VALUE immediate_sweep, VALUE compact)
{
@ -9860,6 +9880,17 @@ gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE
return Qnil;
}
void
rb_gc_prepare_heap(void)
{
gc_start_internal(NULL, Qtrue, Qtrue, Qtrue, Qtrue, Qtrue);
/* The transient heap need to be evacuated before we promote objects */
rb_transient_heap_start_marking(true);
rb_transient_heap_evacuate();
rb_objspace_each_objects(gc_promote_object_i, NULL);
}
static int
gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj)
{

Просмотреть файл

@ -214,6 +214,7 @@ extern VALUE *ruby_initial_gc_stress_ptr;
extern int ruby_disable_gc;
RUBY_ATTR_MALLOC void *ruby_mimmalloc(size_t size);
void ruby_mimfree(void *ptr);
void rb_gc_prepare_heap(void);
void rb_objspace_set_event_hook(const rb_event_flag_t event);
VALUE rb_objspace_gc_enable(struct rb_objspace *);
VALUE rb_objspace_gc_disable(struct rb_objspace *);

Просмотреть файл

@ -115,6 +115,7 @@ int initgroups(const char *, rb_gid_t);
#include "ruby/thread.h"
#include "ruby/util.h"
#include "vm_core.h"
#include "vm_sync.h"
#include "ruby/ractor.h"
/* define system APIs */
@ -8510,6 +8511,37 @@ static VALUE rb_mProcUID;
static VALUE rb_mProcGID;
static VALUE rb_mProcID_Syscall;
/*
* call-seq:
* Process.warmup -> true
*
* Notify the Ruby virtual machine that the boot sequence is finished,
* and that now is a good time to optimize the application. This is useful
* for long running applications.
*
* This method is expected to be called at the end of the application boot.
* If the application is deployed using a pre-forking model, +Process.warmup+
* should be called in the original process before the first fork.
*
* The actual optimizations performed are entirely implementation specific
* and may change in the future without notice.
*
* On CRuby, +Process.warmup+:
*
* * Perform a major GC.
* * Compacts the heap.
* * Promotes all surviving objects to the old generation.
*/
static VALUE
proc_warmup(VALUE _)
{
RB_VM_LOCK_ENTER();
rb_gc_prepare_heap();
RB_VM_LOCK_LEAVE();
return Qtrue;
}
/*
* Document-module: Process
@ -8627,6 +8659,8 @@ InitVM_process(void)
rb_define_module_function(rb_mProcess, "getpriority", proc_getpriority, 2);
rb_define_module_function(rb_mProcess, "setpriority", proc_setpriority, 3);
rb_define_module_function(rb_mProcess, "warmup", proc_warmup, 0);
#ifdef HAVE_GETPRIORITY
/* see Process.setpriority */
rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS));

Просмотреть файл

@ -0,0 +1,11 @@
require_relative '../../spec_helper'
describe "Process.warmup" do
ruby_version_is "3.3" do
# The behavior is entirely implementation dependant.
# Other implementations are free to just make it a noop
it "is implemented" do
Process.warmup.should == true
end
end
end

Просмотреть файл

@ -4,6 +4,7 @@ require 'test/unit'
require 'tempfile'
require 'timeout'
require 'rbconfig'
require 'objspace'
class TestProcess < Test::Unit::TestCase
RUBY = EnvUtil.rubybin
@ -2679,4 +2680,11 @@ EOS
end
end;
end if Process.respond_to?(:_fork)
def test_warmup_promote_all_objects_to_oldgen
obj = Object.new
refute_includes(ObjectSpace.dump(obj), '"old":true')
Process.warmup
assert_includes(ObjectSpace.dump(obj), '"old":true')
end
end