From 885d781abcfce71458b705d227cb719b8d045214 Mon Sep 17 00:00:00 2001 From: ko1 Date: Fri, 21 Aug 2015 11:30:24 +0000 Subject: [PATCH] * ext/objspace/objspace.c: add a new method ObjectSpace.count_symbols. [Feature #11158] * symbol.c (rb_sym_immortal_count): added to count immortal symbols. * symbol.h: ditto. * test/objspace/test_objspace.rb: add a test for this method. * NEWS: describe about this method. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51654 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 13 ++++++ NEWS | 1 + ext/objspace/objspace.c | 80 ++++++++++++++++++++++++++++++++++ symbol.c | 6 +++ symbol.h | 5 +++ test/objspace/test_objspace.rb | 11 +++++ 6 files changed, 116 insertions(+) diff --git a/ChangeLog b/ChangeLog index 6f6b87cf29..41292ba604 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Fri Aug 21 19:58:48 2015 Koichi Sasada + + * ext/objspace/objspace.c: add a new method ObjectSpace.count_symbols. + [Feature #11158] + + * symbol.c (rb_sym_immortal_count): added to count immortal symbols. + + * symbol.h: ditto. + + * test/objspace/test_objspace.rb: add a test for this method. + + * NEWS: describe about this method. + Fri Aug 21 19:48:17 2015 Nobuyoshi Nakada * win32/Makefile.sub ($(LIBRUBY_SO)): needs additional libraries diff --git a/NEWS b/NEWS index 7bdf9b8bd8..1803ed99a8 100644 --- a/NEWS +++ b/NEWS @@ -106,6 +106,7 @@ with all sufficient information, see the ChangeLog file. GC overhead * ObjectSpace (objspace) + * ObjectSpace.count_symbols is added. * ObjectSpace.count_imemo_objects is added. * ObjectSpace.internal_class_of is added. * ObjectSpace.internal_super_of is added. diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 08d41b8365..eff9d0c7f8 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -18,6 +18,7 @@ #include #include "node.h" #include "gc.h" +#include "symbol.h" /* * call-seq: @@ -249,6 +250,84 @@ count_objects_size(int argc, VALUE *argv, VALUE os) return hash; } +struct dynamic_symbol_counts { + size_t mortal; + size_t immortal; +}; + +static int +cs_i(void *vstart, void *vend, size_t stride, void *n) +{ + struct dynamic_symbol_counts *counts = (struct dynamic_symbol_counts *)n; + VALUE v = (VALUE)vstart; + + for (; v != (VALUE)vend; v += stride) { + if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_SYMBOL) { + ID id = RSYMBOL(v)->id; + if ((id & ~ID_SCOPE_MASK) == 0) { + counts->mortal++; + } + else { + counts->immortal++; + } + } + } + + return 0; +} + +size_t rb_sym_immortal_count(void); + +/* + * call-seq: + * ObjectSpace.count_symbols([result_hash]) -> hash + * + * Counts symbols for each Symbol type. + * + * This method is only for MRI developers interested in performance and memory + * usage of Ruby programs. + * + * If the optional argument, result_hash, is given, it is overwritten and + * returned. This is intended to avoid probe effect. + * + * Note: + * The contents of the returned hash is implementation defined. + * It may be changed in future. + * + * This method is only expected to work with C Ruby. + * + * On this version of MRI, they have 3 types of Symbols (and 1 total counts). + * + * * mortal_dynamic_symbol: GC target symbols (collected by GC) + * * immortal_dynamic_symbol: Immortal symbols promoted from dynamic symbols (do not collected by GC) + * * immortal_static_symbol: Immortal symbols (do not collected by GC) + * * immortal_symbol: total immortal symbols (immortal_dynamic_symbol+immortal_static_symbol) + */ + +static VALUE +count_symbols(int argc, VALUE *argv, VALUE os) +{ + struct dynamic_symbol_counts dynamic_counts = {0, 0}; + VALUE hash = setup_hash(argc, argv); + + size_t immortal_symbols = rb_sym_immortal_count(); + rb_objspace_each_objects(cs_i, &dynamic_counts); + + if (hash == Qnil) { + hash = rb_hash_new(); + } + else if (!RHASH_EMPTY_P(hash)) { + st_foreach(RHASH_TBL(hash), set_zero_i, hash); + } + + rb_hash_aset(hash, ID2SYM(rb_intern("mortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.mortal)); + rb_hash_aset(hash, ID2SYM(rb_intern("immortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.immortal)); + rb_hash_aset(hash, ID2SYM(rb_intern("immortal_static_symbol")), SIZET2NUM(immortal_symbols - dynamic_counts.immortal)); + rb_hash_aset(hash, ID2SYM(rb_intern("immortal_symbol")), SIZET2NUM(immortal_symbols)); + + return hash; +} + static int cn_i(void *vstart, void *vend, size_t stride, void *n) { @@ -887,6 +966,7 @@ Init_objspace(void) rb_define_module_function(rb_mObjSpace, "memsize_of_all", memsize_of_all_m, -1); rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1); + rb_define_module_function(rb_mObjSpace, "count_symbols", count_symbols, -1); rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1); rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1); rb_define_module_function(rb_mObjSpace, "count_imemo_objects", count_imemo_objects, -1); diff --git a/symbol.c b/symbol.c index 0167fd8dcb..cd67c53663 100644 --- a/symbol.c +++ b/symbol.c @@ -864,6 +864,12 @@ rb_sym_all_symbols(void) return ary; } +size_t +rb_sym_immortal_count(void) +{ + return (size_t)global_symbols.last_id; +} + int rb_is_const_id(ID id) { diff --git a/symbol.h b/symbol.h index 001d6de1f8..7d95491ee6 100644 --- a/symbol.h +++ b/symbol.h @@ -100,4 +100,9 @@ is_global_name_punct(const int c) ID rb_intern_cstr_without_pindown(const char *, long, rb_encoding *); +RUBY_SYMBOL_EXPORT_BEGIN + +size_t rb_sym_immortal_count(void); + +RUBY_SYMBOL_EXPORT_END #endif diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 3036b879bb..2acc35469f 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -351,4 +351,15 @@ class TestObjSpace < Test::Unit::TestCase } assert_operator i, :>, 0 end + + def test_count_symbols + syms = (1..128).map{|i| ("xyzzy#{i}" * 128).to_sym} + c = Class.new{define_method(syms[-1]){}} + + h = ObjectSpace.count_symbols + assert_operator h[:mortal_dynamic_symbol], :>=, 128, h.inspect + assert_operator h[:immortal_dynamic_symbol], :>=, 1, h.inspect + assert_operator h[:immortal_static_symbol], :>=, Object.methods.size, h.inspect + assert_equal h[:immortal_symbol], h[:immortal_dynamic_symbol] + h[:immortal_static_symbol], h.inspect + end end