inf:ruby:internals
Table of Contents
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
mutexlub 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 by 127.0.0.1
