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. –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%.”
  2. 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
  3. DECOMPILE $ erlc +to_asm foo.erl $ mix decompile Foo --to

    asm https://github.com/michalmuskala/decompile https://happi.github.io/theBeamBook/
  4. 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.
  5. 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.
  6. 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}.
  7. LITERALS test() -> [1, 2, #{a := 5}, foo]. {function,

    test, 0, 2}. {label,2}. {move,{literal,[1,2,#{a => 5},foo]},{x,0}}. return.
  8. 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}}.
  9. 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(<<unquote(char), rest::binary>>, base, count) when base > unquote(digit) do count_digits_nosign(rest, base, count + 1) end end defp count_digits_nosign(<<_::binary>>, _, count), do: count
  10. 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
  11. 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: []
  12. def html_escape_to_iodata(data) when is_binary(data), do: to_iodata(data, 0, data, []) escapes

    = [{?<, "&lt;"}, {?>, "&gt;"}, {?&, "&amp;"}, {?\", "&quot;"}, {?\', "&#39;"}] for {match, insert} <- escapes do defp to_iodata(<<unquote(match), rest::bits>>, 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(<<unquote(match), rest::bits>>, 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)]
  13. PREFER BIFS TO FUNCTIONS • :binary.part to binary_part • :maps.get

    to pattern matching to map_get • :maps.put to syntax