User Tools

Site Tools


inf:ruby:internals

Jak ruby uruchamia mój kod?

Dzielenie kodu na tokeny

1.9.3-p392 :003 > pp Ripper.tokenize("a = 1 + 2")
["a", " ", "=", " ", "1", " ", "+", " ", "2"]

Przekształcenie tokenów na coś, co będzie zrozumiałe dla interpretera

 > pp Ripper.lex("a = 1 + 2")
[[[1, 0], :on_ident, "a"],
 [[1, 1], :on_sp, " "],
 [[1, 2], :on_op, "="],
 [[1, 3], :on_sp, " "],
 [[1, 4], :on_int, "1"],
 [[1, 5], :on_sp, " "],
 [[1, 6], :on_op, "+"],
 [[1, 7], :on_sp, " "],
 [[1, 8], :on_int, "2"]]

Przekształcenie kodu na drzewko AST (abstract syntax tree)

1.9.3-p392 :009 > pp Ripper.sexp "a = 1 + 2"
[:program,
 [[:assign,
   [:var_field, [:@ident, "a", [1, 0]]],
   [:binary, [:@int, "1", [1, 4]], :+, [:@int, "2", [1, 8]]]]]]
 => [:program, [[:assign, [:var_field, [:@ident, "a", [1, 0]]], [:binary, [:@int, "1", [1, 4]], :+, [:@int, "2", [1, 8]]]]]]

Kompilacja drzewka AST w kod bajtowy

Maszyna wirtualna

Przeprowadza dwie operacje: kompilację i interpretację. Podczas kompilacji, maszyna ewaluuje AST i przekształca go w kod bajtowy. Nastęþnie, kod bajtowy jest uruchamiany przez interpreter. Maszyna również obsługuje wielowątkowość i rozszerzenia.

1.9.3-p392 :014 > puts RubyVM::InstructionSequence.compile("a = 1 + 2").disasm
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] a          
0000 trace            1                                               (   1)
0002 putobject        1
0004 putobject        2
0006 opt_plus         <ic:1>
0008 dup              
0009 setlocal         a
0011 leave 
1.9.3-p392 :016 > pp RubyVM::InstructionSequence.compile("a = 1 + 2").to_a
["YARVInstructionSequence/SimpleDataFormat",
 1,
 2,
 1,
 {:arg_size=>0, :local_size=>2, :stack_max=>2},
 "<compiled>",
 "<compiled>",
 nil,
 1,
 :top,
 [:a],
 0,
 [],
 [1,
  [:trace, 1],
  [:putobject, 1],
  [:putobject, 2],
  [:opt_plus, 1],
  [:dup],
  [:setlocal, 2],
  [:leave]]]

INFO

Wielowątkowość

Wielowątkowość to uruchamianie kodu w kilku procesach.

Sekwencyjne uruchamianie:

  • Szybkie odpowiedzi muszą czekać na te wolniejsze,
  • przepustowość jest zależna od kolejki,
  • nie skaluje się, gdy obciążenie wzrasta,

Wątek:

  • kod uruchamiany równocześnie z innym i korzystający ze wspólnej pamięci,

Wielowątkowe uruchamianie:

  • Szybkie odpowiedzi przychodzą pierwsze,
  • przepustowość zależy od średniego czasu odpowiedzi,
  • czas odpowiedzi niewiele się zmienia przy wzrastającym obciążeniu,
  • zbyt wiele wątków może spowolnić serwer.
  • Procesor może wykonywać tylko jedną instrukcję naraz,
  • wielowątkowość jest zaimplementowana za pomocą techniki zmieniania kontekstu (ang. context switching). Zmienianie kontekstu polega na tym, że procesor przeskakuje z wykonywania jednego kawałka kodu do drugiego. Takie zmienianie jest szybsze niż przeskakiwanie pomiędzy procesami,
  • w Rubym wykorzystywany jest ruby fair scheduler, który każdemu kawałkowi daje 10 milisekund,
  • gdy w wątku jest operacja blokująca, scheduler będzie czekał na odblokowanie,
  • od Rubiego 1.9, stosowane są wątki natywne:
    • mogą być uruchamiane na wieloprocesorowych komputerach,
    • wykorzystywany jest scheduler systemu operacyjnego,
    • blokujące operacje I/O nie blokują innych wątków,
  • wątki muszą komunikować się ze sobą za pomocą współdzielonej pamięci co wymusza stosowanie technik mutex lub blokad, aby nie doprowadzić do błędów w pamięci,
  • czasem pojawiają się niedeterministyczne zachowania,
  • w Rubym 1.9 dodano również włókna (ang. fibers):
    • korzystając z włókien, to programista decyduje o strategii przyznawania czasu procesora wątkom,
  • GIL (ang. Global Interpreter Lock):
    • tylko jeden wątek może komunikować się z wirtualną maszyną naraz. Jeśli posiadamy tylko jeden procesor to wszystko jest ok, problem pojawia się, gdy mamy ich więcej
    • ułatwia pracę programistom - trudniej o błędy danych,
    • zapobiegnięcie hazardowi (ang. Race Condition) w rozszerzeniach napisanych w C,
    • ułatwia pisanie rozszerzeń w C,
    • większość rozszerzeń nie obsługuje wielowątkowości,
    • części implementacji Rubego (Hash) nie obsługują wielowątkowości,
    • wiele implementacji nie posiada GIL,
inf/ruby/internals.txt · Last modified: 2021/02/16 09:56 (external edit)