Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Optimising for the BEAM

Optimising for the BEAM

Presentation from Code BEAM STO on optimising Elixir and Erlang programs.

Michał Muskała

May 31, 2018
Tweet

More Decks by Michał Muskała

Other Decks in Programming

Transcript

  1. OPTIMISING FOR THE BEAM

    View Slide

  2. –Donald Knuth
    “premature optimization is the root of all evil”

    View Slide

  3. –Donald Knuth
    “We should forget about small efficiencies, say about 97% of the time:
    premature optimization is the root of all evil. Yet we should
    not pass up our opportunities in that critical 3%.”

    View Slide

  4. MICHAŁ MUSKAŁA
    Elixir & Ecto core team
    http://michal.muskala.eu/
    https://github.com/michalmuskala/
    @michalmuskala

    View Slide

  5. OTP 21

    View Slide

  6. WHY PERFORMANCE MATTERS

    View Slide

  7. LEEX WITH BINARIES?
    https://github.com/michalmuskala/panacea

    View Slide

  8. deflexer :lexer do
    defrule ignored,
    do: :skip
    defrule punctuator, meta(token: token, line: line),
    do: {:token, {String.to_atom(token), line}}
    defrule reserved_word, meta(token: token, line: line),
    do: {:token, {String.to_atom(token), line}}
    defrule int_value, meta(token: token, line: line),
    do: {:token, {:int_value, line, String.to_integer(token)}}
    defrule float_value, meta(token: token, line: line),
    do: {:token, {:float_value, line, String.to_float(token)}}
    defrule string_value, meta(token: token, line: line),
    do: {:token, {:string_value, line, token}}
    defrule boolean_value, meta(token: token, line: line),
    do: {:token, {:boolean_value, line, String.to_atom(token)}}
    defrule name, meta(token: token, line: line),
    do: {:token, {:name, line, token}}
    end

    View Slide

  9. CONTRIBUTION TO OTP & ELIXIR

    View Slide

  10. JASON
    https://hex.pm/packages/jason

    View Slide

  11. MEASURE-LEARN-REFACTOR

    View Slide

  12. MONITOR

    View Slide

  13. MONITOR
    observer
    observer_cli
    wobserver
    https://github.com/zhongwencool/observer_cli
    https://github.com/shinyscorpion/wobserver
    WombatOAM https://www.erlang-solutions.com/products/wombatoam.html

    View Slide

  14. View Slide

  15. View Slide

  16. MEASURE

    View Slide

  17. MEASURE
    fprof
    eprof
    cprof
    mix profile._prof
    xprof
    https://github.com/Appliscale/xprof

    View Slide

  18. View Slide

  19. MICROBENCHMARK

    View Slide

  20. MICROBENCHMARK
    :timer.tc/1
    eministat
    benchee https://github.com/pragtob/benchee
    https://github.com/jlouis/eministat

    View Slide

  21. View Slide

  22. View Slide

  23. NOT IN THE SHELL!!!

    View Slide

  24. DISASSEMBLE

    View Slide

  25. DECOMPILE
    $ erlc +to_asm foo.erl
    $ mix decompile Foo --to asm
    https://github.com/michalmuskala/decompile
    https://happi.github.io/theBeamBook/

    View Slide

  26. UNDERSTAND THE BEAM

    View Slide

  27. REGISTER MACHINE

    View Slide

  28. https://markfaction.wordpress.com/2012/07/15/stack-based-vs-register-based-virtual-machine-architecture-and-the-dalvik-vm/
    STACK VS REGISTER VM

    View Slide

  29. SIMPLE MATHS

    View Slide

  30. SIMPLE MATHS
    def test(a, b), do: a + b * 5
    {function, test, 2, 10}.
    {label,10}.
    {gc_bif,'*',{f,0},2,[{x,1},{integer,5}],{x,1}}.
    {gc_bif,'+',{f,0},2,[{x,0},{x,1}],{x,0}}.
    return.

    View Slide

  31. STACK ALLOCATION & TAIL RECURSION

    View Slide

  32. STACK ALLOCATION & TAIL RECURSION
    defp id(x), do: x
    def test(a, b), do: id(a) + id(b)
    {function, id, 1, 10}.
    {label,10}.
    return.
    {function, test, 2, 12}.
    {label,12}.
    {allocate,1,2}.
    {move,{x,1},{y,0}}.
    {call,1,{f,10}}.
    {move,{x,0},{x,1}}.
    {move,{y,0},{x,0}}.
    {move,{x,1},{y,0}}.
    {call,1,{f,10}}.
    {gc_bif,'+',{f,0},1,[{y,0},{x,0}],{x,0}}.
    {deallocate,1}.
    return.

    View Slide

  33. STACK ALLOCATION & TAIL RECURSION
    defp id(x), do: x
    def test(a, b), do: id(id(a) + id(b))
    {function, test, 2, 12}.
    {label,12}.
    {allocate,1,2}.
    {move,{x,1},{y,0}}.
    {call,1,{f,10}}.
    {move,{x,0},{x,1}}.
    {move,{y,0},{x,0}}.
    {move,{x,1},{y,0}}.
    {call,1,{f,10}}.
    {gc_bif,'+',{f,0},1,[{y,0},{x,0}],{x,0}}.
    {call_last,1,{f,10},1}.

    View Slide

  34. LITERALS

    View Slide

  35. LITERALS
    test() -> [1, 2, #{a := 5}, foo].
    {function, test, 0, 2}.
    {label,2}.
    {move,{literal,[1,2,#{a => 5},foo]},{x,0}}.
    return.

    View Slide

  36. INTEGER MATCHING - JUMP TABLE

    View Slide

  37. INTEGER MATCHING - JUMP TABLE
    def test(x) do
    case x do
    0 -> "zero"
    1 -> "one"
    2 -> "two"
    end
    end
    {function, test, 1, 10}.
    {label,10}.
    {test,is_integer,{f,14},[{x,0}]}.
    {select_val,{x,0},{f,14},
    {list,[{integer,0},{f,11},
    {integer,1},{f,12},
    {integer,2},{f,13}]}}.
    {label,11}.
    {move,{literal,<<"zero">>},{x,0}}.
    return.
    {label,12}.
    {move,{literal,<<"one">>},{x,0}}.
    return.
    {label,13}.
    {move,{literal,<<"two">>},{x,0}}.
    return.
    {label,14}.
    {case_end,{x,0}}.

    View Slide

  38. PERFORMANCE TIPS

    View Slide

  39. MACRO-INLINING
    digits = [{?0..?9, -?0}, {?A..?Z, 10 - ?A}, {?a..?z, 10 - ?a}]
    for {chars, diff} <- digits,
    char <- chars do
    digit = char + diff
    defp count_digits_nosign(<>, base, count)
    when base > unquote(digit) do
    count_digits_nosign(rest, base, count + 1)
    end
    end
    defp count_digits_nosign(<<_::binary>>, _, count), do: count

    View Slide

  40. DELEGATE TO BIFS
    def parse(binary, base) do
    case count_digits(binary, base) do
    0 ->
    :error
    count ->
    {digits, rem} = :erlang.split_binary(binary, count)
    {:erlang.binary_to_integer(digits, base), rem}
    end
    end

    View Slide

  41. REUSE DATA IF NOT MODIFIED
    def delete(keywords, key) when is_list(keywords) and is_atom(key) do
    :lists.filter(fn {k, _} -> k != key end, keywords)
    end
    def delete(keywords, key) when is_list(keywords) and is_atom(key) do
    case :lists.keymember(key, 1, keywords) do
    true -> delete_key(keywords, key)
    _ -> keywords
    end
    end
    defp delete_key([{key, _} | tail], key), do: delete_key(tail, key)
    defp delete_key([{_, _} = pair | tail], key), do: [pair | delete_key(tail, key)]
    defp delete_key([], _key), do: []

    View Slide

  42. PROCESS BINARIES IN CHUNKS

    View Slide

  43. def html_escape_to_iodata(data) when is_binary(data), do: to_iodata(data, 0, data, [])
    escapes = [{?<, "<"}, {?>, ">"}, {?&, "&"}, {?\", """}, {?\', "'"}]
    for {match, insert} <- escapes do
    defp to_iodata(<>, skip, original, acc) do
    to_iodata(rest, skip + 1, original, [acc | unquote(insert)])
    end
    end
    defp to_iodata(<<_char, rest::bits>>, skip, original, acc),
    do: to_iodata(rest, skip, original, acc, 1)
    defp to_iodata(<<>>, _skip, _original, acc),
    do: acc
    for {match, insert} <- escapes do
    defp to_iodata(<>, skip, original, acc, len) do
    part = binary_part(original, skip, len)
    to_iodata(rest, skip + len + 1, original, [acc, part | unquote(insert)])
    end
    end
    defp to_iodata(<<_c, rest::bits>>, skip, orig, acc, len),
    do: to_iodata(rest, skip, orig, acc, len + 1)
    defp to_iodata(<<>>, 0, orig, _acc, _len),
    do: original
    defp to_iodata(<<>>, s, original, acc, len),
    do:[acc | binary_part(original, skip, len)]

    View Slide

  44. DON’T USE DICT - USE MAPS

    View Slide

  45. PREFER BIFS TO FUNCTIONS

    View Slide

  46. PREFER BIFS TO FUNCTIONS
    • :binary.part to binary_part
    • :maps.get to pattern matching to map_get
    • :maps.put to syntax

    View Slide

  47. DREAMING ABOUT FUTURE

    View Slide

  48. TAIL CALLS MODULO CONS

    View Slide

  49. TAIL CALLS MODULO CONS
    map(F, [H|T]) ->
    [F(H)|map(F, T)];
    map(F, []) when is_function(F, 1) -> [].

    View Slide

  50. USE SSE INSTRUCTIONS IN SOME PARTS
    OF BEAM

    View Slide

  51. UNROLLED LINKED LISTS

    View Slide

  52. STORE DATA IN LITERAL AREA
    https://github.com/discordapp/fastglobal inspired by mochiglobal

    View Slide

  53. MULTIPLE RETURN

    View Slide

  54. OPTIMISING FOR THE BEAM

    View Slide