Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rubinius Eurucamp 2012 Workshop

Rubinius Eurucamp 2012 Workshop

Slides for the Rubinius workshop given at Eurucamp 2012

Dirkjan Bussink

August 17, 2012
Tweet

More Decks by Dirkjan Bussink

Other Decks in Programming

Transcript

  1. % cumulative self self total time seconds seconds calls ms/call

    ms/call name ------------------------------------------------------------ 30.12 4.06 2.14 4093941 0.00 0.00 String#downcase 25.72 1.92 1.82 4093941 0.00 0.00 String#transform 21.34 5.50 1.51 298010 0.01 0.02 Object::__script__<14> {} 6.25 0.64 0.44 298020 0.00 0.00 Time#<=> 4.15 7.04 0.29 1 294.15 7036.42 Benchmark::ips<153> {}
  2. % cumulative self self total time seconds seconds calls ms/call

    ms/call name ------------------------------------------------------------ 42.18 5.89 3.02 207592 0.01 0.03 String#upcase! 21.60 1.88 1.55 4982208 0.00 0.00 Rubinius::CType.islower 5.38 0.49 0.39 207678 0.00 0.00 String#initialize_copy 4.89 0.73 0.35 207643 0.00 0.00 Rubinius::ByteArray#dup 3.16 0.34 0.23 4982208 0.00 0.00 Rubinius::ByteArray#[] 3.06 0.34 0.22 4982240 0.00 0.00 Fixnum#>= 2.24 6.69 0.16 207592 0.00 0.03 String#upcase 2.09 0.88 0.15 207730 0.00 0.00 String#modify! 2.08 0.20 0.15 207674 0.00 0.00 Rubinius::ByteArray.new 2.02 0.64 0.14 207645 0.00 0.00 String#dup 1.90 0.16 0.14 207643 0.00 0.00 Kernel#initialize_copy 1.40 6.79 0.10 50092 0.00 0.14 Object::__script__<13> {} 1.24 0.13 0.09 50102 0.00 0.00 Time#<=> 0.82 7.11 0.06 1 58.61 7108.64 Benchmark::ips<153> {}
  3. [BUG: ObjectHeader claimed to be empty or already containing object

    id] 2 rbx 0x0000000101ec9f73 rubinius::bug(char const*) + 67 3 rbx 0x0000000101f8ec43 rubinius::ObjectHeader::set_object_id(rubinius::State*, rubinius::ObjectMemory*, unsigned int) + 121 4 rbx 0x0000000101f8832d rubinius::ObjectMemory::assign_object_id(rubinius::State*, rubinius::Object*) + 127 5 rbx 0x0000000101ff0a26 rubinius::Object::id(rubinius::State*) + 84 6 rbx 0x0000000101f61500 rubinius::Primitives::object_id(rubinius::State*, rubinius::CallFrame*, rubinius::Executable*, rubinius::Module*, rubinius::Arguments&) + 212 7 ??? 0x000000010485a680 0x0 + 4370835072 8 rbx 0x0000000101ed26ec rubinius::VMMethod::interpreter(rubinius::State*, rubinius::VMMethod*, rubinius::InterpreterCallFrame*) + 9708 9 rbx 0x0000000101fbeb66 rubinius::Object* rubinius::VMMethod::execute_specialized<rubinius::OneArgument>(rubinius::State*, rubinius::CallFrame*, rubinius::Executable*, rubinius::Module*, rubinius::Arguments&) + 838 10 rbx 0x0000000101ff07b4 rubinius::Object::send_prim(rubinius::State*, rubinius::CallFrame*, rubinius::Executable*, rubinius::Module*, rubinius::Arguments&, rubinius::Symbol*) + 282 11 rbx 0x0000000101ff07f8 rubinius::Object::private_send_prim(rubinius::State*, rubinius::CallFrame*, rubinius::Executable*, rubinius::Module*, rubinius::Arguments&) + 28 12 rbx 0x0000000101f628b0 rubinius::Primitives::object_send(rubinius::State*, rubinius::CallFrame*, rubinius::Executable*, rubinius::Module*, rubinius::Arguments&) + 130 13 rbx 0x0000000101ed25a6 rubinius::VMMethod::interpreter(rubinius::State*, rubinius::VMMethod*, rubinius::InterpreterCallFrame*) + 9382 14 rbx 0x0000000101fc6241 rubinius::BlockEnvironment::execute_interpreter(rubinius::State*, rubinius::CallFrame*, rubinius::BlockEnvironment*, rubinius::Arguments&, rubinius::BlockInvocation&) + 1013 15 rbx 0x0000000101fc6378 rubinius::BlockEnvironment::invoke(rubinius::State*, rubinius::CallFrame*, rubinius::BlockEnvironment*, rubinius::Arguments&, rubinius::BlockInvocation&) + 260 16 rbx 0x0000000101fc6682 rubinius::BlockEnvironment::call(rubinius::State*, rubinius::CallFrame*, rubinius::Arguments&, int) + 68 17 rbx 0x0000000101ed38be rubinius::VMMethod::interpreter(rubinius::State*, rubinius::VMMethod*, rubinius::InterpreterCallFrame*) + 14270
  4. // Run only while om's lock is held. void ObjectHeader::set_object_id(STATE,

    ObjectMemory* om, uint32_t id) { // Just ignore trying to reset it to 0 for now. if(id == 0) return; // Construct 2 new headers: one is the version we hope that // is in use and the other is what we want it to be. The CAS // the new one into place. HeaderWord orig = header; orig.f.inflated = 0; orig.f.meaning = eAuxWordEmpty; orig.f.aux_word = 0; HeaderWord new_val = orig; new_val.f.meaning = eAuxWordObjID; new_val.f.aux_word = id; if(header.atomic_set(orig, new_val)) return; orig = header; if(orig.f.inflated) { ObjectHeader::header_to_inflated_header(orig)->set_object_id(id); return; } switch(orig.f.meaning) { case eAuxWordEmpty: case eAuxWordObjID: rubinius::bug("ObjectHeader claimed to be empty or already containing object id"); case eAuxWordLock: case eAuxWordHandle: // not inflated, and the aux_word is being used for locking // or a handle. // Inflate! om->inflate_for_id(state, this, id); } }
  5. [BUG: ObjectHeader claimed to be empty or already containing object

    id] 2 rbx 0x0000000101ec9f73 rubinius::bug(char const*) + 67 3 rbx 0x0000000101f8ec43 rubinius::ObjectHeader::set_object_id(rubinius::State*, rubinius::ObjectMemory*, 4 rbx 0x0000000101f8832d rubinius::ObjectMemory::assign_object_id(rubinius::State*, rubinius::Object*) + 1
  6. bt (#nr) - Get the backtrace (#nr lines) frame #nr

    - Jump to frame #nr in backtrace p *obj - Inspect the object it points to p args - Look at the specific object p call_frame->print_backtrace(state, #nr) - Get Ruby backtrace (#nr lines) Cheat sheet <rubinius::ObjectHeader> = { header = { f = { ... Forwarded = 0, ... }, ... }, klass_ = 0x7f83fd58b6a0, ivars_ = 0x1a, __body__ = {0x0} Points to forward if Forwarded == 1 Arguments { name_ = 0xab6, recv_ = 0x102583c90, block_ = 0x1a, total_ = 1, arguments_ = 0x7fff5ee8e868, argument_container_ = 0x0 } Receiver # arguments p *args.arguments_[0]
  7. def all? if block_given? each { |*e| return false unless

    yield(*e) } else each { return false unless Rubinius.single_block_arg } end true end ./bin/rbx compile -B kernel/common/enumerable19.rb
  8. ================ :all? ================= Arguments: 0 required, 0 post, 0 total

    Arity: 0 Locals: 0 Stack size: 2 Line: 100 Lines to IP: 101: 0..2, 102: 3..11, 104: 12..18, 0: 19..19, 106: 20..21 0000: push_has_block 0001: goto_if_false 12 0003: push_self 0004: create_block #<Rubinius::CompiledMethod all? file=kernel/common/enumerable.rb> 0006: allow_private 0007: send_stack_with_block :each, 0 0010: goto 19 0012: push_self 0013: create_block #<Rubinius::CompiledMethod all? file=kernel/common/enumerable.rb> 0015: allow_private 0016: send_stack_with_block :each, 0 0019: pop 0020: push_true 0021: ret ----------------------------------------
  9. ================ :all? ================= Arguments: 0 required, 0 post, 0 total,

    (splat 0) Arity: -1 Locals: 1: e Stack size: 3 Line: 102 Lines to IP: 0: 16..16 0000: cast_for_splat_block_arg 0001: set_local 0 0003: pop 0004: push_local 0 0006: cast_array 0007: yield_splat 0 0009: goto_if_false 14 0011: push_nil 0012: goto 16 0014: push_false 0015: raise_return 0016: ret ---------------------------------------- ================ :all? ================= Arguments: 0 required, 0 post, 0 total, (splat -2) Arity: -1 Locals: 0 Stack size: 2 Line: 104 Lines to IP: 0: 8..8 0000: cast_for_single_block_arg 0001: goto_if_false 6 0003: push_nil 0004: goto 8 0006: push_false 0007: raise_return 0008: ret ----------------------------------------
  10. String* buffer = String::create_pinned(state, bytes); OnStack<1> variables(state, buffer); ssize_t bytes_read;

    native_int t = type->to_native(); retry: state->vm()->interrupt_with_signal(); state->vm()->thread->sleep(state, cTrue); { GCIndependent guard(state, calling_environment); bytes_read = recvfrom(descriptor()->to_native(), (char*)buffer->byte_address(), size, flags->to_native(), (struct sockaddr*)buf, &alen); } state->vm()->thread->sleep(state, cFalse); state->vm()->clear_waiter(); buffer->unpin();
  11. instruction cast_for_splat_block_arg() [ -- arguments ] if(!call_frame->arguments) { Exception::internal_error(state, call_frame,

    "no arguments object"); RUN_EXCEPTION(); } if(call_frame->arguments->total() == 1) { Object* obj = call_frame->arguments->get_argument(0); if(!kind_of<Array>(obj)) { /* Yes, you are reading this code correctly: In Ruby 1.8, calling a * block with these forms { |*| } and { |*a| } with a single argument * that is not an Array and which responds to #to_ary will cause #to_ary * to be called and its return value ignored. Ultimately, the original * object itself is wrapped in an Array and passed to the block. */ if(CBOOL(obj->respond_to(state, state->symbol("to_ary"), cFalse))) { Object* ignored = obj->send(state, call_frame, state->symbol("to_ary")); if(!ignored->nil_p() && !kind_of<Array>(ignored)) { Exception::type_error(state, "to_ary must return an Array", call_frame); RUN_EXCEPTION(); } } } Array* ary = Array::create(state, 1); ary->set(state, 0, obj); stack_push(ary); } else { Array* ary = Array::create(state, call_frame->arguments->total()); for(size_t i = 0; i < call_frame->arguments->total(); i++) { ary->set(state, i, call_frame->arguments->get_argument(i)); } stack_push(ary); } end Method send! Object* on stack Object* reused!
  12. diff --git i/vm/instructions.cpp w/vm/instructions.cpp index 2c1b940..18b678a 100644 --- i/vm/instructions.cpp +++

    w/vm/instructions.cpp @@ -22,6 +22,7 @@ #include "builtin/cache.hpp" #include "call_frame.hpp" +#include "on_stack.hpp" #include "objectmemory.hpp" #include "arguments.hpp" diff --git i/vm/instructions.def w/vm/instructions.def index dbbc68e..65e6052 100644 --- i/vm/instructions.def +++ w/vm/instructions.def @@ -1290,6 +1290,7 @@ instruction cast_for_splat_block_arg() [ -- arguments ] * object itself is wrapped in an Array and passed to the block. */ if(CBOOL(obj->respond_to(state, state->symbol("to_ary"), cFalse))) { + OnStack<1> os(state, obj); Object* ignored = obj->send(state, call_frame, state->symbol("to_ary")); if(!ignored->nil_p() && !kind_of<Array>(ignored)) { Exception::type_error(state, "to_ary must return an Array", call_frame);