$30 off During Our Annual Pro Sale. View Details »

Fazendo o compilador trabalhar por você: técnicas de manipulação e otimização de código

Fazendo o compilador trabalhar por você: técnicas de manipulação e otimização de código

Slides da apresentação para o QCon Hub: https://www.infoq.com/br/presentations/tecnicas-de-manipulacao-e-otimizacao-de-codigo/

Otimização de código é fundamental no software moderno, transformando o código que escrevemos para representar o negócio em algo que o hardware possa entender e executar de forma eficiente. Nesta sessão você aprenderá sobre como a compilação do código pode ser manipulada. Tendo como exemplo o Quill, você verá como a informação por trás do seu código é lida, interpretada e otimizada.

Juliano Alves

December 28, 2020
Tweet

More Decks by Juliano Alves

Other Decks in Technology

Transcript

  1. Fazendo o compilador trabalhar por você:
    técnicas de manipulação e otimização de
    código
    Juliano Alves
    @vonjuliano
    juliano-alves.com

    View Slide

  2. Quem sou eu?
    ● CTO na broad.app
    Em uma jornada por modularização
    Amante de linguagens funcionais
    ● As maneiras
    Scala, Clojure, Elixir
    ● As clássicas
    Java, C#, Python, Ruby
    @vonjuliano
    juliano-alves.com

    View Slide

  3. https://broad.app

    View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. https://hipsters.tech/scala-hipsters-210/

    View Slide

  10. https://getquill.io
    http://homepages.inf.ed.ac.uk/slindley/papers/practical-theory-of-linq.pdf

    View Slide

  11. Quill: Arquitetura

    View Slide

  12. Quill: Arquitetura

    View Slide

  13. Quill: Arquitetura

    View Slide

  14. Linguagens

    View Slide

  15. Código
    substantivo adjetivo
    limpo

    View Slide

  16. contexto tradução
    regra
    substantivo adjetivo
    substantivo
    adjetivo
    Código limpio
    Clean code
    Código limpo

    View Slide

  17. contexto tradução
    regra
    Quill AST
    quote {
    query[Fruit].filter(f => true)
    }
    SELECT f.* FROM Fruit
    WHERE true
    SELECT f.* FROM Fruit
    WHERE (1 = 1)

    View Slide

  18. Manipulação de código

    View Slide

  19. SELECT c.nome,
    c.idade − g.idade,
    FROM cachorro c,
    gato g
    WHERE c.nome = g.nome
    AND c.idade > g.idade

    View Slide

  20. for {
    c <- cachorros
    g <- gatos
    if (c.nome == g.nome &&
    c.idade > g.idade)
    } yield {
    (c.nome, c.idade - g.idade)
    }
    quote {
    }

    View Slide

  21. Quill: Arquitetura

    View Slide

  22. Abstract syntax trees são
    estruturas de dados usadas em
    compiladores para representar
    a estrutura do código
    ...
    É frequentemente usada como
    uma representação
    intermediária do programa
    https://en.wikipedia.org/wiki/Abstract_syntax_tree

    View Slide

  23. https://stackoverflow.com/questions/14790115/where-can
    -i-learn-about-constructing-asts-for-scala-macros

    View Slide

  24. scala> case class Piloto(nome: String, idade: Int)
    scala> val pilotos = List[Pessoa](Piloto("Rogerinho", 36))
    scala> import reflect.runtime.universe._
    scala> showRaw(reify(pilotos).tree)

    View Slide

  25. Apply(
    TypeApply(
    Select(
    Ident(scala.collection.immutable.List), TermName("apply")
    ),
    List(
    Select(
    Select(Select(Ident($line3.$read), TermName("$iw")), TermName("$iw")),
    TypeName("Piloto")))
    ),
    List(
    Apply(
    Select(Select(Select(Select(Ident($line3.$read), TermName("$iw")),
    TermName("$iw")), TermName("Piloto")),
    TermName("apply")), List(Literal(Constant("Rogerinho")),
    Literal(Constant(36)))))
    )

    View Slide

  26. Quill AST (semi) completa

    View Slide

  27. Quill: Arquitetura

    View Slide

  28. scala> case class Pessoa(id: Int, nome: String, idade: Int)
    scala> case class Endereco(fk: Option[Int], rua: String,
    numero :Int)
    scala> import io.getquill._
    scala> val ctx = new SqlMirrorContext(PostgresDialect,
    Literal)
    scala> import ctx._

    View Slide

  29. scala> val q1 = quote { query[Pessoa] }
    scala> pprint.pprintln(q1.ast)
    Entity("Pessoa", List())
    scala> pprint.pprintln(ctx.run(q1).string)
    "SELECT x.id, x.nome, x.idade FROM Pessoa x"

    View Slide

  30. scala> val q2 = quote { query[Pessoa]
    .filter(_.idade > 25) }
    scala> pprint.pprintln(q2.ast)
    Filter(
    Entity("Pessoa", List()),
    Ident("x1"),
    BinaryOperation(
    Property(Ident("x1"), "idade"), >, Constant(25)
    )
    )
    scala> pprint.pprintln(ctx.run(q2).string)
    "SELECT x1.id, x1.nome, x1.idade FROM Pessoa x1 WHERE x1.idade
    > 25"

    View Slide

  31. scala> val q3 = quote {
    | query[Pessoa].filter(_.idade > 25).map(_.nome)
    | }
    scala> pprint.pprintln(q3.ast)
    Map(
    Filter(
    Entity("Pessoa", List()),
    Ident("x1"),
    BinaryOperation(
    Property(Ident("x1"), "idade"), >, Constant(25))
    ),
    Ident("x2"),
    Property(Ident("x2"), "nome")
    )

    View Slide

  32. scala> pprint.pprintln(ctx.run(q3).string)
    "SELECT x1.nome FROM Pessoa x1 WHERE x1.idade > 25"

    View Slide

  33. scala> val q4 = quote {
    | query[Pessoa]
    | .leftJoin(query[Endereco])
    | .on((p, e) => e.fk.exists(_ == p.id))
    | .map {case (p, e) => (p.nome, a.flatMap(_.fk))}
    | }
    scala> pprint.pprintln(q4.ast)

    View Slide

  34. Map(
    Join(
    LeftJoin,
    Entity("Pessoa", List()),
    Entity("Endereco", List()),
    Ident("p"),
    Ident("e"),
    OptionExists(Property(Ident("e"), "fk"), Ident("x1"),
    BinaryOperation(Ident("x1"), ==, Property(Ident("p"), "id")))
    ),
    Ident("x01"),
    Tuple(
    List(
    Property(Property(Ident("x01"), "_1"), "nome"),
    OptionTableFlatMap(Property(Ident("x01"), "_2"), Ident("x2"),
    Property(Ident("x2"), "fk"))
    )
    )
    )

    View Slide

  35. scala> pprint.pprintln(ctx.run(q4).string)
    "SELECT p.nome, a.fk FROM Pessoa p LEFT JOIN Endereco e
    ON e.fk = p.id"

    View Slide

  36. Informação provida

    View Slide

  37. case class Pessoa(id: Long)
    val q = quote {
    query[Pessoa].map(p =>
    if (true) true else false
    )

    View Slide

  38. contexto
    quote {
    query[Pessoa].map(p =>
    if (true) true else false)
    }
    SELECT CASE WHEN true THEN true ELSE false
    END FROM Pessoa p
    SELECT CASE WHEN 1=1 THEN 1=1 ELSE 1=0 END
    FROM Pessoa p
    SELECT CASE WHEN 1=1 THEN 1 ELSE 0 END
    FROM Pessoa p

    View Slide

  39. Quill AST

    View Slide

  40. sealed trait Ast {
    def quat: Quat
    }
    sealed trait Quat
    class Product(...) extends Quat
    case object Null extends Quat
    case object Generic extends Quat
    case object Unknown extends Quat
    sealed trait Primitive extends Quat
    case object Value extends Primitive
    sealed trait Boolean extends Primitive
    case object BooleanValue extends Boolean
    case object BooleanExpression extends Boolean

    View Slide

  41. Introduza informação
    Não tente "adivinhar"
    Tome cuidado!

    View Slide

  42. Otimizações

    View Slide

  43. scala> case class Cachorro(id: Int, nome: String, idade: Int)
    scala> val q = quote {
    | (valor: String) =>
    | if (valor == "drinks")
    | query[Cachorro].filter(_.idade >= 21)
    | else
    | query[Cachorro]
    | }
    scala> pprint.pprintln(q.ast)

    View Slide

  44. Function(
    List(Ident("valor")),
    If(
    BinaryOperation(Ident("valor"), ==, Constant("drinks")),
    Filter(
    Entity("Cachorro", List()),
    Ident("x1"),
    BinaryOperation(
    Property(Ident("x1"), "idade"), >=, Constant(21))
    ),
    Entity("Cachorro", List())
    )
    )

    View Slide

  45. scala> pprint.pprintln(q("drinks").ast)
    If(
    BinaryOperation(Constant("drinks"), ==, Constant("drinks")),
    Filter(
    Entity("Cachorro", List()),
    Ident("x1"),
    BinaryOperation(
    Property(Ident("x1"), "idade"), >=, Constant(21))
    ),
    Entity("Cachorro", List())
    )

    View Slide

  46. scala> pprint.pprintln(q("drinks").ast)
    Filter(
    Entity("Cachorro", List()),
    Ident("x1"),
    BinaryOperation(
    Property(Ident("x1"), "idade"), >=, Constant(21))
    )
    scala> pprint.pprintln(ctx.run(q("drinks")).string)
    "SELECT x1.id, x1.nome, x1.idade FROM Cachorro x1 WHERE x1.idade
    >= 21"

    View Slide

  47. scala> pprint.pprintln(q("ki-suco").ast)
    Entity("Cachorro", List())
    scala> pprint.pprintln(ctx.run(q("ki-suco")).string)
    "SELECT x1.id, x1.nome, x1.idade FROM Cachorro x1"

    View Slide

  48. Option.orElse
    Option.getOrElse

    View Slide

  49. // FlattenOptionOperation.scala
    ast match {
    // ...
    case OptionGetOrElse(HasBooleanQuat(OptionMap(ast, alias,
    body)), HasBooleanQuat(alternative)) =>
    val expr = BetaReduction(body, alias -> ast)
    val output: Ast = (IsNotNullCheck(ast) +&&+ expr) +||+
    (IsNullCheck(ast) +&&+ alternative)
    apply(output)
    case OptionGetOrElse(ast, body) =>
    apply(If(IsNotNullCheck(ast), ast, body))
    // ...
    }

    View Slide

  50. scala> case class Tarefa(id: Int,
    op1: Option[String], op2: Option[String])
    scala> val q = quote {
    | query[Tarefa].filter(t =>
    | t.op1.orElse(t.op2).exists(_ == "cleanup")
    | )
    | }
    scala> pprint.pprintln(ctx.run(q).string)
    "SELECT t.id, t.op1, t.op2 FROM Tarefa t
    WHERE t.op1 = 'cleanup' AND t.op1 IS NOT NULL OR
    t.op2 = 'cleanup' AND t.op2 IS NOT NULL"

    View Slide

  51. Conclusão

    View Slide

  52. Compiladores são
    amigos
    Estruturas e como funcionam
    Linguagens
    O que acontece no backstage
    Manipulação de código
    Mas somente quando necessário!
    Introduza informação
    Use a informação introduzida para
    "encontrar atalhos"
    Otimização

    View Slide

  53. https://getquill.io
    "This is the project you
    are looking for"

    View Slide

  54. Dúvidas?

    View Slide

  55. Fazendo o compilador trabalhar por você:
    técnicas de manipulação e otimização de
    código
    Juliano Alves
    @vonjuliano
    juliano-alves.com
    Obrigado!

    View Slide