Mis à jour : Cette question contient une erreur qui rend le benchmark sans intérêt. Je vais essayer de faire un meilleur benchmark en comparant les fonctionnalités de base de F# et d'Erlang en matière de concurrence et demander les résultats dans une autre question.
J'essaie de comprendre les caractéristiques de performance d'Erlang et de F#. Je trouve le modèle de concurrence d'Erlang très attrayant, mais je suis enclin à utiliser F# pour des raisons d'interopérabilité. Alors que F# n'offre rien de comparable aux primitives de concurrence d'Erlang - de ce que je peux dire, async et MailboxProcessor ne couvrent qu'une petite partie de ce qu'Erlang fait bien - j'ai essayé de comprendre ce qui est possible dans F# en termes de performances.
Dans le livre Programming Erlang de Joe Armstrong, il fait remarquer que les processus sont très bon marché en Erlang. Il utilise le code suivant (en gros) pour démontrer ce fait :
-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)].
Sur mon Macbook Pro, la création et la destruction de 100 000 processus ( processes:max(100000)
) prend environ 8 microsecondes par processus. Je peux augmenter le nombre de processus un peu plus, mais un million semble casser les choses de façon assez constante.
Connaissant très peu F#, j'ai essayé d'implémenter cet exemple en utilisant async et MailBoxProcessor. Ma tentative, qui peut être erronée, est la suivante :
#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."
En utilisant F# sur Mono, le démarrage et l'arrêt de 100 000 acteurs/processeurs prennent moins de 2 microsecondes par processus, soit environ 4 fois plus vite qu'en Erlang. Le plus important, peut-être, est que je peux passer à des millions de processus sans aucun problème apparent. Lancer 1 ou 2 millions de processus prend toujours environ 2 microsecondes par processus. Le démarrage de 20 millions de processeurs est toujours possible, mais ralentit à environ 6 microsecondes par processus.
Je n'ai pas encore pris le temps de bien comprendre comment F# met en œuvre l'asynchronisme et MailBoxProcessor, mais ces résultats sont encourageants. Y a-t-il quelque chose que je fais terriblement mal ?
Sinon, y a-t-il un endroit où Erlang sera probablement plus performant que F# ? Y a-t-il une raison pour que les primitives de concurrence d'Erlang ne puissent pas être apportées à F# par le biais d'une bibliothèque ?
EDIT : Les chiffres ci-dessus sont faux, en raison de l'erreur que Brian a signalée. Je mettrai à jour la question entière quand je l'aurai corrigée.