2009-09-09 4 views
3

Un processus écouter serveur sur socket async et sur chaque message {tcp, Socket, Bin} prend son tampon et:Combien coûte le binaire de collage (list_to_binary)?

Data = list_to_binary([Buffer, Bin]), 
    {next_state, 'READY', State#state{buffer = Data}}. 

Sur certains événements, il débusque tampon:

'READY'({flush}, #state{buffer = Buffer} = State) -> 
    {reply, {Buffer}, 'READY', State#state{buffer = <<>>}}. 

Est-ce cher ? Peut-être préférable de simplement faire une liste et faire list_to_binary (lists: reverse()) une fois sur flush?

+3

C'est une question mieux traitée par un benchmark que de théoriser sur l'implémentation actuelle. – Christian

+0

Votre configuration nécessite-t-elle que le buffer soit retourné comme un binaire? L'utilisateur n'est-il pas capable de gérer le tampon en «morceaux», ou de gérer le format iodata()? – Zed

Répondre

2

Votre première méthode semble être beaucoup plus lent que votre deuxième méthode (par un facteur d'environ 3000 sur ma plate-forme):

-module(test). 
-export([test/0, performance_test/4]). 

-define(ITERATIONS, 100000). 
-define(NEW_DATA, <<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>>). 

accumulate_1(AccumulatedData, NewData) -> 
    list_to_binary([AccumulatedData, NewData]). 

extract_1(AccumulatedData) -> 
    AccumulatedData. 

accumulate_2(AccumulatedData, NewData) -> 
    [NewData | AccumulatedData]. 

extract_2(AccumulatedData) -> 
    list_to_binary(lists:reverse(AccumulatedData)). 

performance_test(AccumulateFun, ExtractFun) -> 
    {Time, _Result} = timer:tc(test, performance_test, [AccumulateFun, ExtractFun, [], ?ITERATIONS]), 
    io:format("Test run: ~p microseconds~n", [Time]). 

performance_test(_AccumulateFun, ExtractFun, AccumulatedData, _MoreIterations = 0) -> 
    ExtractFun(AccumulatedData); 

performance_test(AccumulateFun, ExtractFun, AccumulatedData, MoreIterations) -> 
    NewAccumulatedData = AccumulateFun(AccumulatedData, ?NEW_DATA), 
    performance_test(AccumulateFun, ExtractFun, NewAccumulatedData, MoreIterations - 1). 

test() -> 
    performance_test(fun accumulate_1/2, fun extract_1/1), 
    performance_test(fun accumulate_2/2, fun extract_2/1), 
    ok. 

Sortie:

7> test:test(). 
Test run: 57204314 microseconds 
Test run: 18996 microseconds 
+0

Bien que vous l'ayez comparé. :) Le benchmark est extrême en ce que vous construisez un binaire de 1 million d'octets en blocs de 10 octets. Le temps de copie augmente avec la taille binaire. Construire une centaine de binaires 10 kb chacun, par incréments de dix octets, devrait être plus rapide. En outre, la liste accumulant ajoute le même petit binaire littéral à la liste, ce qui réduit la pression sur le GC, et a un avantage à cause de cela. – Christian

2

Dans les versions actuelles, le traitement des binaires par l'émulateur a été significativement amélioré, donc maintenant vous pouvez aussi prendre le chemin le plus simple et générer le morceau binaire par morceau:

buffer = #state{buffer = <<Buffer/binary, Bin/binary>>}.

Je ne l'ai pas testé par rapport à l'autre approche, mais cela ne devrait pas être mauvais. Les performances entre différentes implémentations dépendront également du nombre de fois que vous effectuez cette opération sur le même tampon et de la taille de chaque bloc.

+0

http://www.erlang.org/doc/efficiency_guide/binaryhandling.html#4.2 – Christian

Questions connexes