Complex
Ruby concepts
simplified
Matt Aimonetti
RubyConf 2011
RubyConf 2011
what you should know
what you don't have to know
puts 'Hello RubyConf'Tokenized representation:
["puts", " ", "'", "Hello RubyWorld", "'"]Lexed representation:
[[[1, 0], :on_ident, "puts"],
[[1, 4], :on_sp, " "],
[[1, 5], :on_tstring_beg, "'"],
[[1, 6], :on_tstring_content, "Hello RubyWorld"],
[[1, 21],:on_tstring_end, "'"]]
Lexer format: [line number, column], type, token]
[[[1, 0], :on_ident, "puts"],
[[1, 4], :on_sp, " "],
[[1, 5], :on_tstring_beg, "'"],
[[1, 6], :on_tstring_content, "Hello RubyWorld"],
[[1, 21],:on_tstring_end, "'"]]
Abstract Syntax Tree (AST):
[:program,
[[:command,
[:@ident, "puts", [1, 0]],
[:args_add_block,
[[:string_literal,
[:string_content, [:@tstring_content, "Hello RubyWorld",
[1, 6]]]]],
false]]]]
ruby syntax parser
keywords : reserved keywords
-> lex.c : automatically generated
parse.y
-> parse.c : automatically generated
require 'ripper'
require 'pp'
src = "puts 'Hello RubyWorld'"
puts "source: #{src}"
puts "tokenized:"
pp Ripper.tokenize(src)
puts "Lexed:"
pp Ripper.lex(src)
puts "Parsed:"
pp Ripper.sexp(src)
compile.c eval.c eval_error.c eval_jump.c eval_safe.c insns.def : definition of VM instructions iseq.c : implementation of VM::ISeq thread.c : thread management and context switching thread_win32.c : thread implementation thread_pthread.c : ditto vm.c vm_dump.c vm_eval.c vm_exec.c vm_insnhelper.c vm_method.c opt_insns_unif.def : instruction unification opt_operand.def : definitions for optimization
dmyext.c dmydln.c dmyencoding.c id.c inits.c main.c ruby.c version.c gem_prelude.rb prelude.rb
#include <ruby.h>
VALUE mNokogiri ;
VALUE mNokogiriXml ;
VALUE mNokogiriXmlSax ;
mNokogiri = rb_define_module("Nokogiri");
mNokogiriXml = rb_define_module_under(mNokogiri, "XML");
mNokogiriXmlSax = rb_define_module_under(mNokogiriXml, "SAX");
rb_const_set(mNokogiri, rb_intern("LIBXML_ICONV_ENABLED"), Qfalse);
#include <ruby.h>
VALUE rConf = rb_define_module("RubyConf");
rb_define_singleton_method(rConf, "bonjour", c_bonjour, 0);
static VALUE c_bonjour(VALUE self) {
return rb_str_new2("bonjour RubyConf!");
}
rb_define_alloc_func(cMysql2Client, allocate);
static VALUE allocate(VALUE klass) {
VALUE obj;
mysql_client_wrapper * wrapper;
/* current class, C data type, function pointer call when marked,
* function pointer called with when freed, pointer to the data
*/
obj = Data_Make_Struct(klass, mysql_client_wrapper,
rb_mysql_client_mark, rb_mysql_client_free, wrapper);
wrapper->encoding = Qnil;
wrapper->active = 0;
wrapper->reconnect_enabled = 0;
wrapper->closed = 1;
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
return obj;
}
static void rb_mysql_client_mark(void * wrapper) {
mysql_client_wrapper * w = wrapper;
if (w) {
rb_gc_mark(w->encoding);
}
}
static void rb_mysql_client_free(void * ptr) {
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
nogvl_close(wrapper);
xfree(ptr);
}
class Client
def query(id)
Server.dispatch(self, id)
end
def reply(id, response)
print "Response: #{id} | "
end
end
module Server
module_function
def dispatch(client, id)
client.reply(id, fake_response(id))
end
def fake_response(id)
response = ""
n = id.even? ? id+1 : id*999
n.times do
response << ("a".."z").to_a[rand(26)]
end
response
end
end
10.times{|n| Client.new.query(n) }
# Response: 0 | Response: 1 | Response: 2 | Response: 3 | Response: 4 |
# Response: 5 | Response: 6 | Response: 7 | Response: 8 | Response: 9 |
Switch the Server#dispatch method from
def dispatch(client, id) client.reply(id, fake_response(id)) end
to:
def dispatch(client, id)
Thread.new { client.reply(id, fake_response(id)) }
end
Client responses:
10.times{|n| Client.new.query(n) }
Thread.list.last.join
# Response: 0 | Response: 2 | Response: 1 | Response: 4 | Response: 6 |
# Response: 8 | Response: 3 | Response: 7 | Response: 9 | Response: 5 |
100.times{ "RubyConf" }
Allocates 100 string objects
{"location" => "New Orleans"}
Allocates 1 hash and 3 strings
class Foo; end; Foo.new
Allocates 1 node, 2 classes, 1 object
Generating the RDoc documentation takes about 80 seconds on my machine. 30% of that time is spent on GC.
We burn 20% of our front-end CPU on garbage collection.
GC::Profiler.enable # your code puts GC::Profiler.result
Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)
1 0.005 110600 393216 9816 0.24300000000000016032
2 0.006 110560 393216 9816 0.40999999999999975353
3 0.008 110680 393216 9816 1.00400000000000133582
Total Object GC Time(ms)
9816 0.24300000000000016032
9816 0.40999999999999975353
9816
GC.disable
ref = ObjectSpace.count_objects[:T_STRING]
10_000.times{|n| 'test' }
count = ObjectSpace.count_objects[:T_STRING] - ref
puts "#{count} new strings added to memory"
puts ObjectSpace.count_objects.inspect
"10026 new strings added to memory"
{:TOTAL=>24526, :FREE=>308, :T_OBJECT=>8, :T_CLASS=>478,
:T_MODULE=>21, :T_FLOAT=>7, :T_STRING=>16295, :T_REGEXP=>24,
:T_ARRAY=>985, :T_HASH=>16, :T_BIGNUM=>3, :T_FILE=>9,
:T_DATA=>398, :T_MATCH=>108, :T_COMPLEX=>1, :T_NODE=>5846,
:T_ICLASS=>19}
GC run, previous cycle was 255 requests ago.
GC 40 invokes.
Index Total Object GC Time(ms)
1 101432 14.47700000000007314327
2 101432 13.95699999999999718625
3 101432 13.84699999999994268762
4 101432 14.65799999999983782573
5 101432 15.47099999999979047516
6 101432 14.96900000000001007550
7 101432 17.90399999999969793407
8 101432 15.38599999999989975663
9 101432 15.29500000000005854872
10 101432 16.75899999999996836664
11 101432 14.70199999999977080734
[60%] 14414 freed strings.
[12%] 2927 freed arrays.
[9%] 2268 freed big numbers.
[2%] 564 freed hashes.
[1%] 373 freed objects.
[5%] 1351 freed parser nodes (eval usage).
Matt Aimonetti
Slides: http://rubyconf2011.merbist.com
Twitter: @merbist
Blog: http://merbist.com