зеркало из https://github.com/github/ruby.git
Implement `Process.warmup`
[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:
Родитель
a84c99468f
Коммит
ba6ccd8714
7
NEWS.md
7
NEWS.md
|
@ -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
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 *);
|
||||
|
|
34
process.c
34
process.c
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче