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.

78f98f64f525ef979a7f62252c55e760?s=128

Michał Muskała

May 31, 2018
Tweet

More Decks by Michał Muskała

Other Decks in Programming

Transcript

  1. OPTIMISING FOR THE BEAM

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

  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%.”
  4. MICHAŁ MUSKAŁA Elixir & Ecto core team http://michal.muskala.eu/ https://github.com/michalmuskala/ @michalmuskala

  5. OTP 21

  6. WHY PERFORMANCE MATTERS

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

  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
  9. CONTRIBUTION TO OTP & ELIXIR

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

  11. MEASURE-LEARN-REFACTOR

  12. MONITOR

  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

  14. None
  15. None
  16. MEASURE

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

  18. None
  19. MICROBENCHMARK

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

  21. None
  22. None
  23. NOT IN THE SHELL!!!

  24. DISASSEMBLE

  25. DECOMPILE $ erlc +to_asm foo.erl $ mix decompile Foo --to

    asm https://github.com/michalmuskala/decompile https://happi.github.io/theBeamBook/
  26. UNDERSTAND THE BEAM

  27. REGISTER MACHINE

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

  29. SIMPLE MATHS

  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.
  31. STACK ALLOCATION & TAIL RECURSION

  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.
  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}.
  34. LITERALS

  35. LITERALS test() -> [1, 2, #{a := 5}, foo]. {function,

    test, 0, 2}. {label,2}. {move,{literal,[1,2,#{a => 5},foo]},{x,0}}. return.
  36. INTEGER MATCHING - JUMP TABLE

  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}}.
  38. PERFORMANCE TIPS

  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(<<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
  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
  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: []
  42. PROCESS BINARIES IN CHUNKS

  43. 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)]
  44. DON’T USE DICT - USE MAPS

  45. PREFER BIFS TO FUNCTIONS

  46. PREFER BIFS TO FUNCTIONS • :binary.part to binary_part • :maps.get

    to pattern matching to map_get • :maps.put to syntax
  47. DREAMING ABOUT FUTURE

  48. TAIL CALLS MODULO CONS

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

    []) when is_function(F, 1) -> [].
  50. USE SSE INSTRUCTIONS IN SOME PARTS OF BEAM

  51. UNROLLED LINKED LISTS

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

  53. MULTIPLE RETURN

  54. OPTIMISING FOR THE BEAM