2015-05-21

9) Spark lazy execution

#bigdata #hadoop #spark

Uma característica pouco compreendida no processamento de Big Data em Spark é a “execução preguiçosa” (lazy execution). A princípio é de se desconfiar que uma característica com nome de “preguiçosa” possa ser de alguma forma útil e adequada no caso de computação, mas como se verá, trata-se de algo muito bom para o desempenho de execução.

Um engenheiro de software, quando escreve código que chama uma determinada função, imagina que o programa vai executar a tal função no momento em que chegar na linha que a chama. É assim na execução tradicional (não preguiçosa). Mas em Spark não é assim que funciona. Dentre os componentes da API do Spark está o “Spark Core”, que implementa as funcionalidades mais básicas, incluindo o agendar, distribuir e monitorar as tarefas sobre os workers do cluster, cuidar da recuperação no caso de falha de um worker, gerência de memória e outros. A classe mais usada para código em Spark é a RDD (resilient distributed datasets), que logicamente representa uma coleção de itens de tipo genérico. Como o nome diz, um RDD é distribuído (tira proveito da distribuição de tarefas pelos workers do cluster), e resiliente (resiste a eventual falha de um worker).

Os programas para Spark são baseados em SparkContext (sc), que reconhece as características do cluster onde os workers estão. Um RDD é criado levando em conta o sc, o que garante que conseguirá interagir com o cluster. Em Spark, o software representa os dados em RDDs, e transforma RDDs em outros RDDs usando os métodos existentes na API do Spark, eventualmente passando funções como argumentos dos métodos. Sem trabalho explícito do programador, o Spark Core distribui os RDDs pelo cluster. Cada RDD é imutável (isso é, de apenas leitura). A criação de um RDD é feita ou a partir de se carregar um dataset externo, ou de forma programática. Uma vez criados, os RDD podem ser alvo de 2 tipos de operações: transformações e ações. Um exemplo de transformação de RDD é filtrar os dados para que cumpram um critério. Um caso seria filtrar as linhas que contém a palavra “ERROR”. Outro caso seria filtrar os campos cujo valor numérico é maior que um argumento dado. Lembre-se que um RDD é uma coleção de um tipo genérico, isso é, pode ser uma coleção de strings ou uma coleção de pares chave-valor.

Muitas vezes, escreve-se um algoritmo a partir de criar-se um RDD carregando um dataset externo, e sobre esse RDD aplica-se um conjunto de transformações. Esse conjunto de transformações é registrado pelo Spark, mas não é executado imediatamente, devido ao conceito sofisticado de execução preguiçosa. Apesar de soar bizarro, o fato de a execução ser preguiçosa faz muito sentido no caso de dados volumosos. Por exemplo: quando se cria um RDD a partir de um dataset muito volumoso, e sobre esse RDD se faz um conjunto de filtragens que faz diminuir muito o tamanho resultante, é possível diminuir o consumo de recursos computacionais (tarefas executadas, dados em memória, etc) por se otimizar o conjunto de transformações de RDD. É menos custoso executar o conjunto otimizado de transformações de RDD que executar cada transformação tomada isoladamente. Essa otimização é transparente para o programador.

Quando o código encontra uma ação aplicada sobre um RDD, então o Spark é forçado a efetivamente executar todo o conjunto de transformações e produzir a saída. Em outras palavras, apenas apenas quando uma ação ocorre é que o Spark deixa de ser “preguiçoso”. Dentre os exemplos de ações incluem-se a produção de saídas em console ou em disco.

Aplicações Spark simples, tal como word count, consistem em apenas criar um RDD a partir de dataset externo, transformá-lo algumas vezes e produzir a saída. Aplicações mais sofisticadas tem várias fontes de dados, e produzem várias saídas. Nesses casos mais sofisticados, pode-se melhorar o desempenho de execução acrescentando códigos para cacheamento de RDDs intermediários. O sistema Spark sem interferência de código de cacheamento cria um registro de transformações de RDD para cada ação que se deseja. Caso existam duas ações que tem uma parte do processamento comum, o Spark sem cache fará o processamento idêntico mais de uma vez. Nesses casos pode-se melhorar o desempenho escrevendo código que faça cache da parte comum.


--------------------------------------------------------------------------------
Sergio Barbosa Villas-Boas (sbVB), Ph.D.
software development, Big Data, cloud, mobile, IoT, HPC, optimization
sbvillasboas@gmail.com, sbvb@poli.ufrj.br
Skype: sbvbsbvb
http://www.sbVB.com.br
https://www.linkedin.com/in/sbvbsbvb
+55-21-97699-1337

Nenhum comentário:

Postar um comentário