Обновлено: в этом вопросе содержится ошибка, из-за которой тест не имеет смысла. Я попытаюсь улучшить тест, сравнивая основные функции параллелизма F # и Эрланга, и поинтересуюсь результатами в другом вопросе.
Я пытаюсь понять рабочие характеристики Erlang и F #. Я считаю модель параллелизма Эрланга очень привлекательной, но я склонен использовать F # по соображениям совместимости. В то время как из коробки F # не предлагает ничего похожего на примитивы параллелизма Erlang - из того, что я могу сказать, 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 # реализует асинхронный и MailBoxProcessor, но эти результаты обнадеживают. Что-то я делаю ужасно неправильно?
Если нет, есть ли место, где Эрланг, вероятно, превзойдет F #? Есть ли какая-то причина, по которой примитивы параллелизма Эрланга не могут быть доставлены в F # через библиотеку?
РЕДАКТИРОВАТЬ: вышеуказанные цифры неверны, из-за ошибки Брайана указал. Я буду обновлять весь вопрос, когда я это исправлю.