Czy F # jest naprawdę szybszy od Erlanga w procesie tarła i zabijania?
Aktualizacja: to pytanie zawiera błąd, który sprawia, że benchmark nie ma znaczenia. Spróbuję lepszego benchmarka porównującego podstawową funkcjonalność współbieżności F# i Erlanga i zapytam o wyniki w innym pytaniu.
Staram się zrozumieć charakterystykę Erlanga i F#. Uważam, że model współbieżności Erlanga jest bardzo atrakcyjny, ale jestem skłonny używać F# ze względów interoperacyjności. Podczas gdy po wyjęciu z pudełka F # nie oferuje niczego podobnego do Erlanga współbieżność - z tego co mogę powiedzieć async i MailboxProcessor obejmują tylko niewielką część tego co Erlang robi dobrze - próbowałem zrozumieć co jest możliwe w F# performance wise. W książce Joe Armstrong "Programowanie Erlanga" zwraca uwagę, że procesy w Erlangu są bardzo tanie. Używa (z grubsza) następującego kodu, aby zademonstrować ten fakt:-module(processes).
-export([max/1]).
%% max(N)
%% Create N processes then destroy them
%% See how much time this takes
max(N) ->
statistics(runtime),
statistics(wall_clock),
L = for(1, N, fun() -> spawn(fun() -> wait() end) end),
{_, Time1} = statistics(runtime),
{_, Time2} = statistics(wall_clock),
lists:foreach(fun(Pid) -> Pid ! die end, L),
U1 = Time1 * 1000 / N,
U2 = Time2 * 1000 / N,
io:format("Process spawn time=~p (~p) microseconds~n",
[U1, U2]).
wait() ->
receive
die -> void
end.
for(N, N, F) -> [F()];
for(I, N, F) -> [F()|for(I+1, N, F)].
Na moim MacBooku Pro zrobienie i zabicie 100 tysięcy procesów (processes:max(100000)
) zajmuje około 8 mikrosekund na procesy. Mogę podnieść liczbę procesów nieco dalej, ale milion wydaje się łamać rzeczy dość konsekwentnie.
Znając bardzo mało F#, próbowałem zaimplementować ten przykład używając async i MailBoxProcessor. Moja próba, która może być błędna, wygląda następująco:
#r "System.dll"
open System.Diagnostics
type waitMsg =
| Die
let wait =
MailboxProcessor.Start(fun inbox ->
let rec loop =
async { let! msg = inbox.Receive()
match msg with
| Die -> return() }
loop)
let max N =
printfn "Started!"
let stopwatch = new Stopwatch()
stopwatch.Start()
let actors = [for i in 1 .. N do yield wait]
for actor in actors do
actor.Post(Die)
stopwatch.Stop()
printfn "Process spawn time=%f microseconds." (stopwatch.Elapsed.TotalMilliseconds * 1000.0 / float(N))
printfn "Done."
Używanie F# na Mono, uruchomienie i zabicie 100 000 aktorów/procesorów zajmuje mniej niż 2 mikrosekundy na proces, około 4 razy szybciej niż Erlang. Co ważniejsze, być może, mogę zwiększyć skalę do milionów procesów bez widocznych problemów. Uruchomienie 1 lub 2 milionów procesów trwa nadal około 2 mikrosekund na proces. Uruchomienie 20 milionów procesorów jest nadal możliwe, ale spowalnia do około 6 mikrosekund na proces.
Nie poświęciłem jeszcze czasu, aby w pełni zrozumieć, jak F# implementuje async i procesor MailBoxProcessor, ale te wyniki są zachęcające. Czy jest coś, co robię strasznie źle?
Jeśli nie, to czy jest jakieś miejsce, gdzie Erlang prawdopodobnie przewyższy F#? Na jest jakiś powód, dla którego Erlang nie może być przeniesiony do F# przez Bibliotekę?
EDIT: powyższe liczby są błędne, z powodu błędu, na który wskazywał Brian. Zaktualizuję całe pytanie, kiedy to naprawię.
2 answers
W oryginalnym kodzie uruchomiłeś tylko jeden procesor skrzynki pocztowej. Stwórz wait()
funkcję i wywołaj ją z każdym yield
. Również nie czekasz na nich, aby zakręcić lub otrzymać wiadomości, które myślę, że unieważnia informacje o czasie; Zobacz mój kod poniżej.
W zasadzie każdy ' suspended async '( czyli stan, w którym Skrzynka pocztowa czeka na linii jak
let! msg = inbox.Receive()
) zajmuje tylko pewną liczbę bajtów, gdy jest zablokowany. Dlatego możesz mieć znacznie więcej asynchronicznych niż wątków; wątek zazwyczaj zajmuje megabajt pamięci lub więcej.
Ok, oto kod, którego używam. Możesz użyć małej liczby, takiej jak 10, i --define DEBUG, aby upewnić się, że semantyka programu jest tym, co jest pożądane (wyjścia printf mogą być przeplatane, ale dostaniesz pomysł).open System.Diagnostics
let MAX = 100000
type waitMsg =
| Die
let mutable countDown = MAX
let mre = new System.Threading.ManualResetEvent(false)
let wait(i) =
MailboxProcessor.Start(fun inbox ->
let rec loop =
async {
#if DEBUG
printfn "I am mbox #%d" i
#endif
if System.Threading.Interlocked.Decrement(&countDown) = 0 then
mre.Set() |> ignore
let! msg = inbox.Receive()
match msg with
| Die ->
#if DEBUG
printfn "mbox #%d died" i
#endif
if System.Threading.Interlocked.Decrement(&countDown) = 0 then
mre.Set() |> ignore
return() }
loop)
let max N =
printfn "Started!"
let stopwatch = new Stopwatch()
stopwatch.Start()
let actors = [for i in 1 .. N do yield wait(i)]
mre.WaitOne() |> ignore // ensure they have all spun up
mre.Reset() |> ignore
countDown <- MAX
for actor in actors do
actor.Post(Die)
mre.WaitOne() |> ignore // ensure they have all got the message
stopwatch.Stop()
printfn "Process spawn time=%f microseconds." (stopwatch.Elapsed.TotalMilliseconds * 1000.0 / float(N))
printfn "Done."
max MAX
[4]] to wszystko powiedziane, Nie wiem Erlang, i nie myślałem głęboko o tym, czy jest sposób, aby przyciąć F #już (choć jest to dość idiomatyczne jak-jest).Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-06 23:48:54
VM Erlanga nie używa wątków OS ani procesu, aby przełączyć się na nowy proces Erlanga. To VM po prostu zlicza wywołania funkcji do Twojego kodu / procesu i przeskakuje do innego procesu VM po niektórych (do tego samego procesu OS i tego samego wątku OS).
CLR wykorzystuje mechanikę opartą na procesie OS i wątkach, więc F # ma znacznie wyższe koszty ogólne dla każdego przełącznika kontekstowego.
Więc odpowiedź na twoje pytanie brzmi: "nie, Erlang jest znacznie szybszy niż tarło i procesy zabijania".
P. S. możesz znaleźć wyniki praktycznego konkursu ciekawe.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-01 19:04:41