====== 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: @>==========
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
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},
"",
"",
nil,
1,
:top,
[:a],
0,
[],
[1,
[:trace, 1],
[:putobject, 1],
[:putobject, 2],
[:opt_plus, 1],
[:dup],
[:setlocal, 2],
[:leave]]]
===== INFO =====
* http://rubyonrails.pl/forum/t6340-VM-rubiego---jak-dzia%B3a
* https://speakerdeck.com/jan/milion-rzeczy-ktore-musi-zrobic-yarv-zanim-wykona-twoj-kod?slide=6
* http://patshaughnessy.net/2012/6/29/how-ruby-executes-your-code
* http://patshaughnessy.net/2012/6/18/the-start-of-a-long-journey-how-ruby-parses-and-compiles-your-code
* http://www.youtube.com/watch?v=Qxoc1wrjBuE
====== 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'',