====== 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'',