Обновлено: этот вопрос содержит ошибку, которая делает этот тест бессмысленным. Я попытаюсь использовать более эффективный тест, сравнивающий функциональные возможности F # и Erlang basic concurrency и узнайте о результатах в другом вопросе.
Я пытаюсь понять характеристики производительности Erlang и F #. Я нахожу модель Erlang concurrency очень привлекательной, но я склонен использовать F # для соображений совместимости. Несмотря на то, что F # не предлагает ничего подобного примитивам Erlang concurrency - из того, что я могу сказать, async и MailboxProcessor охватывают только небольшую часть того, что делает Erlang, - я пытался понять, что возможно в F # производительность.
В книге Джо Эрнланга "Программирование Эрланг" он подчеркивает, что в Эрланге процессы очень дешевы. Он использует (примерно) следующий код, чтобы продемонстрировать этот факт:
-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)].
На моем Macbook Pro нерестится и убивает 100 тысяч процессов (processes:max(100000)
) занимает около 8 микросекунд на каждый процесс. Я могу немного увеличить количество процессов, но миллион, похоже, довольно быстро нарушает работу.
Зная очень мало F #, я попытался реализовать этот пример, используя async и MailBoxProcessor. Моя попытка, которая может быть неправильной, заключается в следующем:
#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."
Используя F # на Mono, запуск и убийство 100 000 участников/процессоров занимает менее 2 микросекунд на процесс, примерно в 4 раза быстрее, чем у Erlang. Что еще более важно, возможно, это то, что я могу масштабировать до миллионов процессов без каких-либо очевидных проблем. Начиная 1 или 2 миллиона процессов все еще занимает около 2 микросекунд на процесс. Начиная с 20 миллионов процессоров по-прежнему возможно, но замедляется примерно до 6 микросекунд на каждый процесс.
Я еще не нашел времени, чтобы полностью понять, как F # реализует async и MailBoxProcessor, но эти результаты обнадеживают. Есть ли что-то, что я делаю ужасно неправильно?
Если нет, есть ли место, где Erlang, скорее всего, превзойдет F #? Есть ли причина, по которой примитивы Erlang concurrency не могут быть перенесены в F # через библиотеку?
EDIT: приведенные выше цифры ошибочны, из-за ошибки, которую указал Брайан. Я буду обновлять весь вопрос, когда я его исправлю.