2016-11-03 1 views
0

J'essaie de comparer un algo de quicksorting contre Enum.sort. Je reçois des résultats très différents de benchfella et benchwarmer.Elixir Benchfella & Benchwarmer ont des résultats radicalement différents

Résumé:

   Benchfella  Benchwarmer 
Enum.sort  8920.47 µs/op 2418767.00 μs/op 
QuickSort  16660.45 µs/op  15745.04 μs/op 

Détails:

Voici ce que je suis l'analyse comparative:

defmodule Thing do 
    defstruct [:key] 
end 

defmodule QuickSort do 
    def qsort([]), do: [] 
    def qsort([pivot | rest]) do 
    {left, right} = Enum.partition(rest, fn(x) -> x.key < pivot.key end) 
    qsort(left) ++ [pivot] ++ qsort(right) 
    end 
end 

defmodule Bench do 
    use Benchfella 
    Benchfella.start 

    bench "QuickSort", [list: gen()] do 
    QuickSort.qsort(list) 
    end 

    bench "Enum.sort", [list: gen()] do 
    Enum.sort(list, fn(x, y) -> x.key > y.key end) 
    end 

def gen, do: for _ <- 1..10000, do: %Thing{key: :rand.uniform} 

    # Tests 
    list = for _ <- 1..10000, do: %Thing{key: :rand.uniform} 
    sorted = Enum.sort_by(list, &(&1.key)) 
    true = sorted == QuickSort.qsort(list) 
end 

est ici la sortie de Benchfella:

$ mix bench 

Settings: 
    duration:  1.0 s 

## Bench 
[20:57:40] 1/2: Enum.sort 
[20:57:43] 2/2: QuickSort 

Finished in 4.71 seconds 

## Bench 
benchmark iterations average time 
Enum.sort   200 8920.47 µs/op 
QuickSort   100 16660.45 µs/op 

est ici la sortie de Benchwarmer:

$ iex -S mix 
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] 

Interactive Elixir (1.3.3) - press Ctrl+C to exit (type h() ENTER for help) 
iex(1)> list = Bench.gen 
[%Thing{key: 0.29976857621751096}, %Thing{key: 0.42956956935048163}, 
%Thing{key: 0.8682735084348573}, %Thing{key: 0.13149039866062429}, 
%Thing{key: 0.5315758481143932}, %Thing{...}, ...] 

iex(2)> Benchwarmer.benchmark fn -> QuickSort.qsort list end 
*** #Function<20.52032458/0 in :erl_eval.expr/5> *** 
1.9 sec 127 iterations 15745.04 μs/op 

[%Benchwarmer.Results{args: [], duration: 1999619, 
    function: #Function<20.52032458/0 in :erl_eval.expr/5>, n: 127, prev_n: 64}] 

iex(3)> Benchwarmer.benchmark fn -> Enum.sort(list, fn(x, y) -> x.key > y.key end) end 
*** #Function<20.52032458/0 in :erl_eval.expr/5> *** 
2.4 sec  1 iterations 2418767.0 μs/op 

[%Benchwarmer.Results{args: [], duration: 2418767, 
    function: #Function<20.52032458/0 in :erl_eval.expr/5>, n: 1, prev_n: 1}] 
+1

On dirait qu'Erlang est incapable d'effectuer une optimisation pour 'fn' déclaré directement dans le REPL (en dehors d'un module). Je vois des résultats similaires à Benchfella si je mets le code Benchwarmer dans un module puis je l'appelle et je peux aussi reproduire le timing de ~ 2.5 secondes avec ': timer.tc fn -> Enum.sort (list, fn (x, y) -> x.key Dogbert

+1

Vous comparez différents cadres _et_ structure de code différente _ et_ AFAICT même des données différentes. Veuillez retravailler vos exemples afin qu'une seule chose soit différente, sinon il est impossible de faire une comparaison. – cdegroot

+1

Je ne suis pas sûr qu'il soit juste de me donner un -1 pour cette question. Je demande pourquoi «différents cadres et structure de code différente» donnent des résultats différents et auxquels il faut faire confiance. Je pense que c'est une question très valable et que les débutants dans 'Elixir' trouveraient utile. – jdesilvio

Répondre

2

C'est très probablement causé en testant une fonction définie dans la coquille. Dans le shell, le code est évalué, pas compilé. Ne comparez jamais rien d'autre que des modules compilés. Tout benchmarking effectué en dehors des modules compilés est plus ou moins inutile.

+0

Merci. C'est ce que je cherchais. – jdesilvio