desenvolvimento do algoritmo knn em elixir e …
TRANSCRIPT
DESENVOLVIMENTO DO ALGORITMO KNN EM ELIXIR E
BENCHMARKING COM DIFERENTES IMPLEMENTACOES
Carlos Junior Felix Rodrigues
Projeto de Graduacao apresentado ao Curso
de Engenharia Eletronica e de Computacao
da Escola Politecnica, Universidade Federal
do Rio de Janeiro, como parte dos requisitos
necessarios a obtencao do tıtulo de Enge-
nheiro.
Orientador: Flavio Luis de Mello
Rio de Janeiro
Maio de 2021
DESENVOLVIMENTO DO ALGORITMO KNN EM ELIXIR E
BENCHMARKING COM DIFERENTES IMPLEMENTACOES
Carlos Junior Felix Rodrigues
PROJETO DE GRADUACAO SUBMETIDO AO CORPO DOCENTE DO CURSO
DE ENGENHARIA ELETRONICA E DE COMPUTACAO DA ESCOLA PO-
LITECNICA DA UNIVERSIDADE FEDERAL DO RIO DE JANEIRO COMO
PARTE DOS REQUISITOS NECESSARIOS PARA A OBTENCAO DO GRAU
DE ENGENHEIRO ELETRONICO E DE COMPUTACAO
Autor:
Carlos Junior Felix Rodrigues
Orientador:
Prof. Flavio Luıs de Mello, D. Sc.
Examinador:
Prof. Heraldo Luıs Silveira de Almeida, D. Sc.
Examinador:
Paulo Oliveira Lenzi Valente, B. Sc.
Rio de Janeiro
Maio de 2021
ii
Declaracao de Autoria e de Direitos
Eu, Carlos Junior Felix Rodrigues CPF 058.933.523-51, autor da monogra-
fia Desenvolvimento do Algoritmo KNN em Elixir e Benchmarking com Diferentes
Implementacoes, subscrevo para os devidos fins, as seguintes informacoes:
1. O autor declara que o trabalho apresentado na disciplina de Projeto de Gra-
duacao da Escola Politecnica da UFRJ e de sua autoria, sendo original em forma e
conteudo.
2. Excetuam-se do item 1. eventuais transcricoes de texto, figuras, tabelas, conceitos
e ideias, que identifiquem claramente a fonte original, explicitando as autorizacoes
obtidas dos respectivos proprietarios, quando necessarias.
3. O autor permite que a UFRJ, por um prazo indeterminado, efetue em qualquer
mıdia de divulgacao, a publicacao do trabalho academico em sua totalidade, ou em
parte. Essa autorizacao nao envolve onus de qualquer natureza a UFRJ, ou aos seus
representantes.
4. O autor pode, excepcionalmente, encaminhar a Comissao de Projeto de Gra-
duacao, a nao divulgacao do material, por um prazo maximo de 01 (um) ano,
improrrogavel, a contar da data de defesa, desde que o pedido seja justificado, e
solicitado antecipadamente, por escrito, a Congregacao da Escola Politecnica.
5. O autor declara, ainda, ter a capacidade jurıdica para a pratica do presente ato,
assim como ter conhecimento do teor da presente Declaracao, estando ciente das
sancoes e punicoes legais, no que tange a copia parcial, ou total, de obra intelectual,
o que se configura como violacao do direito autoral previsto no Codigo Penal Bra-
sileiro no art.184 e art.299, bem como na Lei 9.610.
6. O autor e o unico responsavel pelo conteudo apresentado nos trabalhos academicos
publicados, nao cabendo a UFRJ, aos seus representantes, ou ao(s) orientador(es),
qualquer responsabilizacao/ indenizacao nesse sentido.
7. Por ser verdade, firmo a presente declaracao.
Nome do aluno
iii
UNIVERSIDADE FEDERAL DO RIO DE JANEIRO
Escola Politecnica - Departamento de Eletronica e de Computacao
Centro de Tecnologia, bloco H, sala H-217, Cidade Universitaria
Rio de Janeiro - RJ CEP 21949-900
Este exemplar e de propriedade da Universidade Federal do Rio de Janeiro, que
podera incluı-lo em base de dados, armazenar em computador, microfilmar ou adotar
qualquer forma de arquivamento.
E permitida a mencao, reproducao parcial ou integral e a transmissao entre bibli-
otecas deste trabalho, sem modificacao de seu texto, em qualquer meio que esteja
ou venha a ser fixado, para pesquisa academica, comentarios e citacoes, desde que
sem finalidade comercial e que seja feita a referencia bibliografica completa.
Os conceitos expressos neste trabalho sao de responsabilidade do(s) autor(es).
iv
DEDICATORIA
Dedico este trabalho a todos que, ao agirem segundo seu verdadeiro proposito,
estao construindo um mundo melhor.
v
AGRADECIMENTO
Gostaria de agradecer a minha famılia, principalmente meu pai, minha mae e
minha irma: Carlos Laeres, Maria Zilma e Karen Luane. Obrigado por sempre me
apoiarem em todas as decisoes da minha vida, sem voces eu jamais teria chegado
onde cheguei.
Tambem gostaria de agradecer ao meu orientador, Flavio Mello, por sua presenca
e dedicacao durante a orientacao deste trabalho.
Nao poderia tambem esquecer de deixar aqui agradecimentos a todos os amigos
que fiz na Ericsson. Todos voces foram demasiadamente importantes para o meu
crescimento profissional.
Agradeco ao time de desenvolvimento do Nx, por sua dedicacao na construcao
desta biblioteca e por sempre serem solıcitos as questoes feitas por mim durante o
desenvolvimento deste trabalho.
Um enorme agradecimento a todas as pessoas queridas que estiveram presente na
minha vida durante este perıodo de formacao. Cada um com qual compartilhei esta
experiencia sabe da importancia do seu papel em meu desenvolvimento.
Por fim, agradeco ao povo brasileiro que contribuiu de forma significativa a mi-
nha formacao e estada nesta Universidade. Este projeto e uma pequena forma de
retribuir o investimento e confianca em mim depositados.
vi
RESUMO
O avanco da computacao tem nos levado a uma era onde e possıvel a analise de
uma grande quantidade de dados por algoritmos de aprendizado de maquina. Tendo
em vista a importancia dessa area, este trabalho apresenta um estudo de desempenho
para a implementacao do algoritmo KNN utilizando a linguagem de programacao
funcional Elixir. Para uma base de comparacao foram utilizados implementacoes
em Python fazendo uso de bibliotecas comumente aplicadas para esta tarefa. Este
estudo pretende, dentro de suas delimitacoes, encontrar uma forma eficiente de se
implementar o algoritmo KNN em Elixir.
Palavras-Chave: Aprendizado de Maquina, KNN, Elixir, Rust, Python.
vii
ABSTRACT
The advancement of computing has taken us to an era where it is possible to
analyze a large amount of data using machine learning algorithms. Considering the
importance of this area, this work presents a benchmark study for the implementa-
tion of the KNN algorithm using the functional programming language Elixir. For a
comparison basis, Python implementations were used with libraries commonly ap-
plied for this task. This study intends, within its delimitation, to find an efficient
way to implement the KNN algorithm in Elixir.
Keywords: Machine Learning, KNN, Elixir, Rust, Python.
viii
SIGLAS
BEAM - Bogdan’s Erlang Abstract Machine
GPU - Graphics Processing Unit
HLO - High Level Operations
KNN - K-Nearest Neighbor
LLVM - Low Level Virtual Machine
NIF - Native Implemented Function
PCIe - Peripheral Component Interconnect Express
XLA - Accelerated Linear Algebra
ix
Sumario
1 Introducao 1
1.1 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Delimitacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Justificativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.4 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.5 Metodologia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.6 Descricao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Fundamentacao Teorica 4
2.1 Algoritmo KNN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Numpy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Tensorflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.4 Elixir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 NIF - Native Implemented Function . . . . . . . . . . . . . . . . . . . 11
2.6 Elixir com Rust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.7 Numerical Elixir (Nx) . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.8 Procedimentos para avaliacao de linguagens de programacao . . . . . 13
3 Avaliacoes 14
3.1 Descricao do experimento . . . . . . . . . . . . . . . . . . . . . . . . 14
3.1.1 Maquina Utilizada . . . . . . . . . . . . . . . . . . . . . . . . 15
3.1.2 Implementacoes em Python . . . . . . . . . . . . . . . . . . . 15
3.1.3 Implementacoes em Elixir . . . . . . . . . . . . . . . . . . . . 16
3.2 Dados de teste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3 Resultados e Analise . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
x
4 Conclusoes 23
4.1 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Bibliografia 25
xi
Lista de Figuras
2.1 Imagem de representacao do KNN. . . . . . . . . . . . . . . . . . . . . . 5
2.2 Diagrama conceitual de uma array Numpy.] . . . . . . . . . . . . . . . . 6
2.3 Diagrama com o processo de compilacao do XLA. . . . . . . . . . . . . . 8
xii
Lista de Tabelas
3.1 Resultado do experimento. . . . . . . . . . . . . . . . . . . . . . . . . 21
xiii
Capıtulo 1
Introducao
1.1 Tema
O presente trabalho trata da implementacao e estudo do algoritmo K Nearest
Neighbours (KNN) para ser executado na linguagem de programacao Elixir. Nesse
contexto, o problema a ser resolvido e investigar o desempenho de diferentes imple-
mentacoes nessa linguagem com outras implementadas em Python.
1.2 Delimitacao
A implementacao do algoritmo se dara para o modelo de forca bruta e considerara
como criterio a distancia euclidiana. Alem disso, para a investigacao foi utilizado
um dataset contendo duas classes e 30 colunas de informacoes.
A escolha deste algoritmo se deu por este ser um dos algoritmos corriqueiros
em Machine Learning para resolver o problema de classificacao[1]. Alem de que,
pode ser aplicado em diversos domınios como saude, ciencia, financas, polıtica e
reconhecimento de imagem e vıdeo.
1.3 Justificativa
A linguagem Elixir[2] tem seu principal uso para a construcao de back-end para
sistemas web. Isso se deve principalmente a conceitos modernos que a linguagem
fornece e ao ferramental presente em sua maquina virtual (Erlang VM ) para a
1
criacao de sistemas escalaveis. Junto a isto esta o crescente uso do paradigma
de programacao funcional, principalmente pela caracterıstica da imutabilidade dos
dados e da nao mudanca de estado, o que previne erros em algoritmos paralelos, ja
que uma variavel compartilhada jamais apresentara diferentes leituras. Elixir e uma
das novas linguagens que vem se destacando dentro deste paradigma[3]. Contudo,
um campo que ainda vem sendo explorado nesta linguagem e o do aprendizado de
maquina. E de interesse a expansao desta subarea para a linguagem Elixir, tendo em
vista que algoritmos de aprendizado de maquina vem sendo cada vez mais utilizados,
principalmente por aplicacoes web.
1.4 Objetivos
O objetivo principal deste trabalho e realizar o desenvolvimento e benchmarking [4]
de um classificador KNN em Elixir. Tendo isto em mente, serao realizadas ava-
liacoes de desempenho entre diferentes implementacoes executadas na Erlang VM
e comparacoes com implementacoes em Python. A fim de saber qual a forma mais
interessante de executar o algoritmo em Elixir e tambem como esta implementacao
se compara com implementacoes na linguagem que hoje e considerada uma das mais
utilizadas para aprendizado de maquina[5].
1.5 Metodologia
A primeira etapa consiste na escolha das formas de implementar o algoritmo em
Elixir. Foram escolhidas 3: utilizando as proprias estruturas de dados da linguagem,
utilizando uma comunicacao com a linguagem Rust[6] para processar os calculos e
utilizando a biblioteca de Elixir Numerico[7].
A segunda etapa segue para o desenvolvimento das implementacoes em Python,
foram escolhidas as bibliotecas Numpy[8] e Tensorflow[9]. Para cada uma dessas
bibliotecas o algoritmo foi implementado de duas formas diferentes.
Com as duas primeiras etapas concluıdas, chegamos a terceira onde serao realiza-
dos testes de desempenho com as diferentes implementacoes.
2
A ultima etapa consiste na conclusao destes testes, concluindo assim com a des-
coberta de qual seria a forma mais efetiva para implementar o KNN em Elixir.
1.6 Descricao
No capıtulo 2 sera apresentada toda a teoria utilizada para a realizacao deste
projeto. Isto inclui desde conceitos de aprendizado de maquina ate linguagens e
bibliotecas utilizadas.
O capıtulo 3 desenvolve as diferentes implementacoes utilizadas para o projeto,
alem de, comparacoes entre as implementacoes. Neste capıtulo tambem sao discu-
tidos os seus resultados.
Por fim, o capıtulo 4 contem a conclusao dos resultados obtidos e propostas para
aprimoramentos futuros.
3
Capıtulo 2
Fundamentacao Teorica
2.1 Algoritmo KNN
O algoritmo k-nearest-neighbors foi descrito pela primeira vez no inıcio dos anos
1950. Devido ao seu elevado custo computacional ao receber grandes conjuntos de
treinamento, ele so veio a se tornar popular posteriormente com o aumento do poder
computacional[10].
O KNN se baseia na hipotese de que dados se encontram proximos em termos da
similaridade dos valores dos atributos entre as amostras. Esta tecnica e utilizada
para encontrar o rotulo de uma amostra nao classificada atraves dos rotulos das
amostras mais proximas vindas de um conjunto de treinamento. Este algoritmo se
torna util quando se deve decidir o rotulo de uma amostra que se encontra em um
limite entre classes. A figura 2.1 exemplifica para um caso em duas dimensoes:
4
Figura 2.1: Imagem de representacao do KNN.
Um conceito importante para a classificacao do KNN e o calculo da distancia
entre o dado nao classificado e os dados de treinamento. Existem varias metricas
de distancia e uma bastante utilizada e a distancia euclidiana. Sejam dois pontos
distintos X1 = (x11, x12, ..., x1,n) e X2 = (x21, x22, ..., x2n), a distancia euclidiana e
dada pela equacao:
dist(X1,X2) =
√√√√ n∑i=1
(x1i − x2i)2 (2.1)
Outro conceito importante e o parametro k o qual decide quantos vizinhos serao
selecionados pelo algoritmo KNN, aumentar o k tende a suavizar os limites de decisao
evitando o ajuste excessivo (overfitting). A decisao do valor de k nao e unica para
todos os modelos de classificacao. Geralmente, principalmente se houver somente
duas classes, e escolhido um numero ımpar para k com o intuito de que o algoritmo
nao entre em um empate.
A tecnica de classificacao KNN possui varias aplicacoes no mundo real, sendo
usada eficientemente para deteccao de outliers, como por exemplo deteccao de fraude
de cartao de credito[11]. Este metodo de classificacao tambem e usado na area
medica para determinar a comparacao de proteınas ou DNA e verificar a relevancia
das informacoes. Em areas de seguranca, a abordagem K-Nearest Neighbors tambem
pode ser usada para reconhecimento de expressao facial[12].
5
Dentre as desvantagens deste algoritmo estao a necessidade de muita memoria
e o alto custo computacional[13]. Para este algoritmo, executado em forca bruta,
temos, durante a inferencia, uma complexidade de tempo de O(n × m), onde n e
o numero de exemplos de treinamento e m e o numero de dimensoes no conjunto
de treinamento[14]. Devido a isto, para nao termos problemas, com o tempo e
necessaria uma execucao eficiente para uma grande quantidade de dados. Essa
complexidade pode ser reduzida consideravelmente com o uso de estruturas BallTree
e KDTree[15].
2.2 Numpy
Numpy e uma biblioteca para Python que traz suporte para arrays multidimen-
sionais, entre outras funcionalidades, provendo diversas operacoes matematicas so-
fisticadas, sendo muito utilizado para a criacao de modelos de Machine Learning.
O Numpy possui um bom desempenho devido ao uso de codigo C pre-compilado e
altamente otimizado[16].
Python e uma linguagem que possibilita a utilizacao de modulos que sejam pacotes
binarios, sendo assim Numpy e construıdo fazendo proveito da linguagem C para
tarefas de mais baixo nıvel que o Python nao daria suporte. Um exemplo disso sao
as arrays do Numpy (ndarray) que apresentam um desempenho superior as listas de
Python pois sao armazenados em um local contınuo na memoria, sendo assim elas
podem ser acessadas e manipuladas por processos de maneira muito mais eficiente.
Figura 2.2: Diagrama conceitual de uma array Numpy. Fonte: [16]
6
A figura 2.2 apresenta um diagrama conceitual que mostra a relacao entre os tres
objetos fundamentais usados para descrever os dados de uma array: 1) o proprio
ndarray, 2) o objeto de tipo de dados que descreve o layout de um unico elemento
de tamanho fixo da array, 3) o objeto Python escalar que e retornado quando um
unico elemento da array e acessado.
Cada item de um ndarray ocupa o mesmo tamanho de bloco de memoria e todos
os blocos sao interpretados exatamente da mesma maneira[17]. A maneira como
cada item da array deve ser interpretado e especificado por um objeto de tipo de
dados separado. Alem dos tipos basicos (int, floats, etc.), os objetos de tipo de
dados tambem podem representar estruturas de dados. Um item extraıdo de uma
array, por exemplo, por indexacao, e representado por um objeto Python cujo tipo
e um tipo escalar construıdos em Numpy. Os escalares de array permitem facil
manipulacao de arranjos de dados tambem mais complexos.
Sendo assim o Numpy demonstra ser uma ferramenta interessante para a imple-
mentacao de um KNN com grandes quantidades de dados, pois fornece uma array
otimizada e operacoes matematicas para realizar nessas estruturas de dados.
2.3 Tensorflow
Tensorflow e uma biblioteca comumente usada em Deep Learning criada pelo
Google e desenvolvida em 2015. A estrutura do tensor e semelhante ao array multi-
dimensional do Numpy[18], com a diferenca que tensores sao estruturas imutaveis.
O runtime do Tensorflow e escrito em C++ e foi construıdo com a finalidade de
executar um bom desempenho em GPUs. Para se tirar vantagem do processamento
de uma GPU e necessario que sejam processados muitos calculos paralelizaveis, ja
que, devido a GPU ser instalada no PCIe, o tempo de comunicacao costuma ser um
pouco maior do que com a CPU[19].
Para obter um bom desempenho o Tensorflow usa um compilador especıfico para
Algebra Linear chamado XLA (Accelerated Linear Algebra)[20].A linguagem de en-
trada para o XLA e denominada HLO (do ingles, operacoes de alto nıvel). O HLO
7
funciona como grafos contendo as operacoes, em teoria de compiladores ele e clas-
sificado como uma Representacao intermediaria[21]. Atraves dos grafos recebidos
no HLO[22] o XLA compila para instrucoes de maquinas distintas para diferentes
arquiteturas. A figura 2.3 apresenta um diagrama mostrando o fluxo de trabalho do
XLA:
Figura 2.3: Diagrama com o processo de compilacao do XLA.
O XLA possui varias otimizacoes e passos de analise que sao independentes do
tipo de arquitetura destino, como por exemplo, eliminacao de sub-expressoes comuns
(CSE) e analise de buffer para alocar memoria de tempo de execucao.
Apos essa etapa o XLA envia o HLO para o back-end para realizar mais oti-
mizacoes, porem desta vez com informacoes e necessidades especıficas do destino
em mente. Por exemplo, o back-end para GPU do XLA pode realizar a fusao de
operacoes beneficas especificamente para o modelo de programacao da GPU e de-
terminar como particionar os processos. Nesse estagio, os back-ends tambem podem
corresponder a certas operacoes ou combinacoes de padroes para otimizar chamadas
de biblioteca.
8
Na ultima etapa ocorre o uso do compilador LLVM para gerar codigo nativo. Isso
consiste no back-end emitir o codigo de representacao intermediaria para o LLVM
(LLVM IR).
Assim como o Numpy o Tensorflow tambem pode ser usado para uma imple-
mentacao do KNN por oferecer otimizacoes para ferramentas matematicas e es-
truturas de dados. Em testes ja realizados de comparacao do Tensorflow com o
Numpy[23][24], a biblioteca Numpy apresentou uma leve vantagem de desempenho
em situacoes em que os dois estejam sendo executados pela CPU, embora neste
trabalho foi observado uma diferenca muito grande de desempenho entre as duas
bibliotecas. Reforcando assim que o Tensorflow se destaca em casos envolvendo
paralelismo com GPU.
2.4 Elixir
Elixir e uma linguagem de programacao brasileira de paradigma funcional que
roda sobre a BEAM (maquina virtual do Erlang, linguagem da Ericsson usada inici-
almente para solucionar problemas de telecomunicacao). Elixir e comumente usado
para sistemas web escalaveis, ja que a BEAM e conhecida por criar aplicacoes dis-
tribuıdas com paralelismo, baixa latencia e tolerancia a falhas.
A maquina virtual do Erlang fornece processos leves para dar contexto ao codigo
em execucao. Esses processos sao chamados de actors e nao compartilham memoria,
mas se comunicam por meio da passagem de mensagens, copiando dados de um
processo para outro[25]. A passagem de mensagens e um recurso que a maquina
virtual implementa por meio de mailboxes pertencentes a cada processo. A passagem
da mensagem e uma operacao sem bloqueio, o que significa que o envio de uma
mensagem para outro processo e quase instantaneo e a execucao do remetente nao
e bloqueada. As mensagens enviadas sao na forma de dados imutaveis, copiados da
pilha do processo de envio para a caixa de correio do destinatario. Isso e feito sem a
necessidade de bloqueio e exclusao mutua entre os processos, apenas um bloqueio na
caixa de correio no caso de varios processos enviarem uma mensagem para o mesmo
destinatario em paralelo. Cada processo consome as mensagens de sua mailbox
9
sequencialmente, como uma FIFO[26].
Outro fator que tem influenciado na adocao do Elixir em aplicacoes e a forca
que o paradigma funcional tem ganhado em sistemas de processamento concorrente.
O paradigma funcional se baseia em imutabilidade e na definicao matematica de
funcao, ou seja o resultado de uma funcao apenas e afetado pelos seus argumentos
e a funcao nao gera nenhum efeito alem do valor que ela retorna[27]. Em contra
ponto, o paradigma imperativo apresenta dados mutaveis, significando que muitas
partes do programa podem fazer referencia ao mesmo dado e seu valor pode mudar.
A mutacao de dados pode ser perigosa para o processamento concorrente, facil-
mente introduzindo bugs difıceis de detectar. Dados imutaveis tambem resultam
em estrategias diferentes para gerenciamento de memoria, uma vez que, garantida a
imutabilidade, e possıvel apontar para espacos na memorias ja alocados para novos
dados.
As desvantagens do Elixir estao ligadas ao fato de a linguagem ainda ter uma
comunidade pequena comparada com outras linguagens como Python, Ruby ou
Java, e tambem por ser uma linguagem relativamente recente. Apesar de tirar
proveito da maturidade da BEAM, a pouca idade da linguagem e um fator influente
no quanto seu ecossistema ainda nao esta completo de bibliotecas para a solucao de
diferentes problemas.
Como exemplo de uma linguagem popular que tambem e executada dentro de uma
maquina virtual robusta, temos a linguagem Java[28]. Em um estudo comparativo
em testes de concorrencia para aplicacoes web, a maquina virtual do Erlang apre-
sentou um desempenho melhor do que a JVM (Java Virtual Machine)[29] alem de
nao apresentar problemas para uma grande quantidade de processos criados. Outro
estudo[30] relacionado a IoT (Internet of Things) mostra o Elixir lidando com mais
requisicoes por segundo que o Java, apesar de fazer um uso maior da CPU.
10
2.5 NIF - Native Implemented Function
Uma NIF trata-se de uma funcao que e chamada pela BEAM, porem sua imple-
mentacao se da por uma linguagem externa a BEAM. E a forma mais simples e
rapida de executar codigo nativo pela maquina virtual do Erlang[31].
Uma NIF que nao pode ser dividida e executada em um milissegundo ou menos e
chamada de dirty NIF, pois executa um trabalho que os schedulers comuns da BEAM
nao conseguem lidar. Ocorre assim um tratamento especial, onde os processos sao
executados em um conjunto separado de schedulers chamados de dirty schedulers,
nao tendo assim a restricao de duracao que uma NIF normal possui[32], ja que seu
chaveamento ocorrera por threads de OS nativo em vez do chaveamento preemptivo
da BEAM.
2.6 Elixir com Rust
Rust e uma linguagem multiparadigma compilada desenvolvida pela Mozilla pro-
jetada para ser rapida e com um eficiente gerenciamento de memoria. Assim como
Elixir, trata-se de uma linguagem nova com desenvolvimento iniciado em 2010[33].
Seu principal objetivo e ser uma linguagem para o desenvolvimento de ferramentas
que exijam desempenho, como sistemas operacionais ou browser engines [34].
Notadamente sua sintaxe possui semelhancas com C e C++, contudo e observada
tambem a influencia do paradigma funcional em seu projeto. Por exemplo, todas
as variaveis sao por padrao imutaveis. Outros exemplos sao a presenca de pattern
matching, tipos algebricos e funcoes de ordem superior. Alem disso, diferente de C
os lacos tipo for apenas trabalham sobre iteradores a fim de evitar problemas de
acesso a ındices fora dos limites[35].
Existe a possibilidade de integrar Rust com Elixir para fazer uso de seu desem-
penho. O caso mais popular relatado desse uso e o do aplicativo de voz sobre IP
Discord [36]. A chamada de um codigo programado em outra linguagem pode ser
feita atraves de uma NIF, sua desvantagem e que o processo que executa a BEAM
no sistema operacional hospedeiro e inteiramente destruıdo caso uma excecao seja
11
levantada no codigo executado pela NIF. Existe a biblioteca Rustler que prove su-
porte a Rust em Elixir atraves de NIFs seguros, garantindo que o processo da BEAM
nao caia e tambem que nao ocorra vazamento de memoria.
Por ser uma linguagem com maior proximidade ao baixo nıvel e por isto mais
performatica que Elixir, pode-se aproveitar para construir uma funcao em Rust
para executar todos os calculos das distancias exigidas no KNN e assim executar em
Elixir atraves do NIF.
Dentro da literatura cientıfica, a linguagem Rust exibiu um desempenho seme-
lhante a linguagem C para o projeto de um Garbage Collector de alta performance[37].
Alem disso em um teste de desempenho para calculo de mapa logıstico em Elixir
o uso da chamada de codigo nativo compilado em Rust atraves da biblioteca Rus-
tler apresentou um ganho de desempenho significativo comparado a implementacao
puramente em Elixir[38].
2.7 Numerical Elixir (Nx)
Nx e uma biblioteca lancada em 2021 de multiestagios de complicacao que fornece
a possibilidade de trabalhar com computacao numerica no Elixir[39], similar ao
NumPy, TensorFlow, ou PyTorch. Nx apresenta a capacidade de trabalhar com
tensores, aumenta a capacidade e o desempenho do Elixir ao trabalhar com numeros
e torna possıvel compilar o codigo para a GPU.
Dentre suas funcionalidades esta um novo tipo de definicao de funcao, defn, que
e um subset da linguagem de programacao Elixir adaptada especificamente para
calculos numericos, implementada em Elixir atraves de macros, sem necessidade
de adicoes ao Kernel da linguagem. Quando as definicoes numericas sao invocadas,
elas sao transformadas em expressoes (internamente Nx.Defn.Expr) que representam
a arvore abstrata sintatica (AST) ou grafo de computacao da definicao numerica.
Essas expressoes sao manipuladas por compiladores para produzir executaveis que
rodam nativamente em CPU ou GPU[40].
12
Existe a possibilidade de incorporar diferente compiladores para o Nx, porem ate
o momento so existe desenvolvido o EXLA que se trata de um binding para Elixir
do XLA, do Tensorflow. No lugar do compilador tambem ha a possibilidade de se
usar um back-end e ate o momento dois back-ends estao implementados para NX, o
BinaryBackend, que e o back-end padrao da biblioteca, e o Torchx que se trata de um
client para o LibTorch (A API em C++ do projeto Pytorch do Facebook). Diferente
de um compilador, o back-end ira fazer com que todas as operacoes do Nx sejam
despachadas para as implementacoes em NIF do Torchx, retornando o controle para
a VM entre cada chamada e percebendo o resultado de cada calculo intermediario
antes de completar o proximo. Ao usar um back-end, ha a possibilidade de dentro
da definicao de funcao numerica (defn) misturar arbitrariamente o codigo Nx com
o codigo Elixir, ja que se trata de uma funcao Elixir regular[41].
2.8 Procedimentos para avaliacao de linguagens
de programacao
A formulacao de uma metodologia para avaliar o desempenho computacional de
uma determinada tarefa se faz necessaria para criar uma justa comparacao. Para
isto temos o conceito de benchmarking sendo aplicado tanto para a industria quanto
para a academia. A definicao do dicionario Oxford[42] afirma que um benchmark e
”Avaliar ou verificar algo por comparacao com um padrao estabelecido”.
A principal metrica usada para comparacao e o tempo. Ao medir o tempo de
execucao estamos analisando tambem alguns fatores que influenciam os resultados
de desempenho. Claro que deve-se considerar um mesmo cenario, pois ao comparar
linguagens que executam em maquinas com recursos de processamento diferentes
levara a resultados diferentes. Alem disso, e interessante realizar varias execucoes
e calcular a media da medicao de tempo, a fim de suavizar pequenas diferencas
causadas pelo sistema operacional[43]. As diferentes implementacoes tambem devem
respeitar as caracterısticas da linguagem ou biblioteca, levando assim a solucao a
refletir o desempenho real da ferramenta.
13
Capıtulo 3
Avaliacoes
3.1 Descricao do experimento
O experimento consistiu em medir o tempo de execucao para 7 implementacoes
diferentes do algoritmo KNN trabalhando sobre um mesmo conjunto de dados de
treinamento e com o intuito de predizer uma amostra. As diferentes implementacoes
sao:
� Python com Numpy utilizando array multidimensional
� Python com Numpy utilizando for-loop
� Python com Tensorflow utilizando array multidimensional
� Python com Tensorflow utilizando for-loop
� Elixir
� Elixir com Rust
� Elixir com Nx
A metrica escolhida para ser usado no KNN foi a distancia Euclideana, por se tra-
tar de uma metrica comumente usada para a distancia entre dois pontos. Entende-se
que neste trabalho, foram realizadas implementacoes proprias do KNN para obter
o controle de variaveis livres. Para o numero de vizinhos mais proximos a serem
considerados foi definido o valor 5, por este ser um valor aproximado do logaritmo
14
na base 10 do total de pontos do conjunto de dados para treinamento. Sendo este
um metodo baseado em uma evidencia empırica para encontrar o valor de K. A fim
de evitar problemas devido a diferencas entre bibliotecas de benchmarking para as
diferentes linguagens usadas neste trabalho, foi optado pela construcao de um codigo
proprio para o benchmarking utilizando modulos nativos de tempo das respectivas
linguagens, foram realizadas 200 medidas para cada implementacao.
Em resumo todas as implementacoes foram feitas seguindo estes passos:
1. Calcular todas as distancias da amostra desconhecida com as amostras de
treinamento
2. Ordenar as distancias e obter as K menores distancias
3. Retornar a classe predominante entre as K menores distancias
3.1.1 Maquina Utilizada
Todos os testes foram realizados numa maquina local. Por questoes praticas,
o processo de compilacao e execucao de cada programa foi feito em um Windows
Subsystem for Linux (WSL)[44], isto e, uma camada de compatibilidade para exe-
cutar binarios do Linux nativamente no Windows. As configuracoes da maquina
utilizada sao:
� Sistema Operacional Host: Windows 10 Pro
� WSL Linux: Ubuntu 20.04.2.0 LTS
� Processador: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz 1.90 GHz
� Memoria RAM: 16,0 GB
3.1.2 Implementacoes em Python
Para cada implementacao usando uma biblioteca em Python (Numpy e Tensor-
flow) foram feitas duas variacoes delas. Uma consistia em realizar todas as operacoes
de calculo da distancia atraves de um loop for em Python e a outra consistia em
no lugar do loop utilizar operacoes com array multidimensional da biblioteca que se
15
esta sendo usada. Como descrito no capıtulo anterior, na sessao sobre essas duas
bibliotecas, e de se esperar diferencas de desempenho[45] uma vez que operacoes
nessas bibliotecas nao sao puramente interpretadas pelo interpretador do Python,
diferente do loop for nativo da linguagem.
#trecho da implementacao em Numpy usando loop-for:
def euclidean(p1, p2):
dist = np.sqrt(np.sum((p1-p2)**2))
return dist
distances = np.fromiter((euclidean(x_train[j, :], i) for j in
range(len(x_train))), dtype=float)
#trecho da implementacao em Numpy usando Array Multidimensional:
distances = np.sqrt(np.sum((np.subtract(x_train, x_input[0])**2), axis=1))
#trecho da implementacao em Tensorflow usando loop-for:
def euclidean(p1, p2):
dist = tf.sqrt(tf.reduce_sum(tf.subtract(p1,p2)**2))
return dist
distances = np.fromiter((euclidean(x_train[j, :], i) for j in
range(len(x_train))), dtype=float)
#trecho da implementacao em Tensorflow usando tensor:
distances = tf.norm(tf.subtract(X_train, x), ord=’euclidean’, axis=1)
3.1.3 Implementacoes em Elixir
A primeira implementacao do KNN em Elixir foi feita utilizando apenas as estru-
turas de dados de lista presentes no Elixir. A funcao predict possui como parametros
x train (a lista contendo todos os atributos dos dados para treino), y train (a lista
contendo a classe dos dados de treino), x input(os atributos do ponto o qual sera
feito a predicao) e o valor k:
16
defp euclidean(p1, p2) do
Enum.zip(p1, p2)
|> Enum.reduce(0, fn {xi, yi}, acc ->
acc + :math.pow(xi - yi, 2)
end)
|> :math.sqrt()
end
def predict(x_train, y_train, x_input, k) do
distances = Enum.map(x_train, &euclidean(&1, x_input))
indices =
distances |> Enum.with_index()
|> Enum.sort_by(fn {val, _idx} -> val end)
|> Enum.take(k)
|> Enum.map(fn {_, idx} -> idx end)
indices |> Enum.map(fn idx -> Enum.at(y_train, idx) end) |> mode()
end
def mode(list) when is_list(list) do
h = hist(list)
max = Map.values(h) |> Enum.max()
h |> Enum.find(fn {_, val} -> val == max end) |> elem(0)
end
def hist(list) when is_list(list) do
list
|> Enum.reduce(%{}, fn tag, acc -> Map.update(acc, tag, 1, &(&1 + 1))
end)
end
Diferente de linguagens de paradigma imperativo, Elixir contem a funcao Enum.map
que substitui o papel do laco for. Sao recebidos dois parametros, sendo o primeiro a
17
lista e o segundo uma funcao que sera executado para cada elemento da lista. Assim
e feito o calculo da distancia por todos os pontos. Apos calcular as distancias, a lista
e alterada para uma tuple contendo a distancia e o respectivo ındice e e ordenada.
Apos isso, retiram-se apenas os ındices dos K menores valores de distancia. Ainda se
faz necessario descobrir quais sao as classes desses ındices atraves da lista y train.
Apos isso a lista com as classes e inserida numa funcao mode que retorna a moda
estatıstica, ou seja o elemento que mais se repetiu nessa lista.
Na segunda implementacao foi utilizada uma funcao Rust de predicao que e cha-
mado pelo Elixir. No codigo Rust foram definidos duas estruturas Point, represen-
tando um ponto com o atributo fields que se trata de um Vetor de Floats de 64
bits e um atributo Class que se trata de inteiro de 8 bits, e Distance, que possui o
valor da distancia em Float 64 bits e a sua classe em inteiro de 8 bits. A funcao que
calcula a distancia Euclidiana recebe um vetor de Points e um vetor de Floats (res-
pectivamente os pontos que serao treinados e o vetor de atributos do ponto que sera
predito). O retorno desta funcao e um Distance, que contem a distancia calculada
e sua classe.
struct Point {
fields: Vec<f64>,
class: i8
}
struct Distance {
value: f64,
class: i8
}
fn euclidean(x: &Point, y: &[f64]) -> Distance {
let mut distance =
x.fields.as_slice().iter()
.zip(y.iter())
.fold(0.0, |s, (&a, &b)| s + (a - b) * (a - b));
Distance {value: distance.sqrt(), class: x.class}
18
}
fn classify(training: &[Point], fields: &[f64], k: i8) -> i8 {
let mut distance = training.iter()
.map(|p| euclidean(p, fields)).collect::<Vec<Distance>>();
distance.sort_by(|a, b| a.value.partial_cmp(&b.value).unwrap());
let indexes = distance[0..k as usize].iter().map(|d|
d.class).collect::<Vec<i8>>();
mode(indexes.as_slice()).unwrap()
}
fn mode(numbers: &[i8]) -> Option<i8> {
let mut counts = HashMap::new();
numbers.iter().copied().max_by_key(|&n| {
let count = counts.entry(n).or_insert(0);
*count += 1;
*count
})
}
Na ultima implementacao utilizando a biblioteca Nx foi necessario separar a
funcao de predicao em duas. Ja que para compilar para o EXLA e necessario criar
uma funcao que apenas receba e retorne o tipo Tensor (com excecao de parametros
de entrada previamente configurado, como o caso do K), alem tambem de nao per-
mitir nenhuma funcao de manipulacao de lista em vez de tensores. Sendo assim
a primeira funcao apenas recebe x train e o y input de tensores, retornando um
tensor contendo os ındices das menores distancias. Na segunda funcao recebendo os
indices e a lista contendo as classes do treinamento, a fim de assim obter as K classes
e usar a funcao estatıstica de moda para retornar o resultado. Nota-se tambem a nao
necessidade do map, ja que e possıvel fazer a subtracao direto pelos dois tensores e
calcular o resultado da distancia aplicando a funcao de norma no tensor resultante.
19
O codigo-fonte com todas as implementacoes se encontra no GitHub.[46]
@defn_compiler {EXLA, client: :default}
defn predictNx1(x_train, x_input, k \\ 5) do
distances =
Nx.subtract(x_train, x_input) |> Nx.LinAlg.norm(axes: [1])
Nx.argsort(distances)[0..k-1]
end
def predictNx2(indices, y_train) do
values = Enum.map(indices, fn index ->
Enum.at(y_train,index)
end)
mode(values)
end
3.2 Dados de teste
O conjunto de dados de teste usados para o experimento contem transacoes feitas
por cartoes de credito em Setembro de 2013 por titulares de cartoes europeus[47].
Este conjunto de dados apresenta transacoes que ocorreram em dois dias, onde
existem 492 fraudes em 284.807 transacoes. O conjunto de dados e desequilibrado,
a classe de fraudes e responsavel por 0,172% de todas as transacoes. Sua coleta e
analise foi realizada durante uma colaboracao entre a empresa Worldine e o grupo de
Machine Learning da Universite Libre de Bruxelles(ULB) para uma pesquisa sobre
big data e deteccao de fraude. Para o treinamento foi separado 80% do conjunto de
dados, resultando em 227.845 pontos.
Este dataset contem 31 colunas contendo apenas variaveis numericas, sendo 28 de-
las resultado de uma transformacao de Analise de Componentes Principais (PCA).
Devido a questoes de confidencialidade, nao foram fornecidos os recursos originais
e mais informacoes basicas sobre os dados. Os unicos campos que nao foram trans-
formados com o PCA sao ’Time’, ’Amount’ e ’Class’. O campo ’Time’ contem os
segundos decorridos entre cada transacao e a primeira transacao no conjunto de
20
dados. O campo ’Amount’ e a quantidade da transacao, este recurso pode ser usado
para aprendizado sensıvel ao custo dependente de exemplo. O campo ’Class’ e a
variavel que contem o rotulo e assume o valor 1 em caso de fraude e 0 em caso nao
seja fraude.
3.3 Resultados e Analise
A tabela 3.1 mostra os resultados considerando o experimento descrito ante-
riormente neste capıtulo.
Tabela 3.1: Resultado do experimento.
Implementacao Media(s) Desvio Padrao(s)
Python com Numpy (array multidimensional) 0.1686 0.0113
Python com Tensorflow (array multidimensional) 0.3848 0.0476
Elixir com Nx 0.3853 0.0603
Elixir com Rust 0.6343 0.0892
Elixir 1.6655 0.2443
Python com Numpy (for-loop) 2.3207 0.4657
Python com Tensorflow (for-loop) 17.0893 0.8180
A partir dos resultados gerados, pode-se perceber em relacao ao Python que a
utilizacao de operacoes em arrays multidimensionais das bibliotecas Numpy e Ten-
sorflow apresenta uma consideravel vantagem em relacao ao uso do loop for presente
na propria linguagem. Como ja descrito no capıtulo anterior, era esperado que o
desempenho usando Numpy fosse superior ao Tensorflow para as condicoes apresen-
tadas neste trabalho.
Para o caso da implementacao apenas utilizando a estrutura de dados de lista do
Elixir e sua funcao Enum.map nativa, uma comparacao que conclui algo interessante
deve ser feita com as implementacoes em python que usam o laco for, ja que nessas
duas implementacoes sao usadas features ja presentes na linguagem para realizar
iteracoes pelo conjunto de dados. Ainda que no caso do Python ele estivesse usando
21
bibliotecas numericas para lidar com os calculos, o Elixir apresentou um desempenho
superior.
O uso da linguagem Rust integrada ao Elixir como forma de oferecer o suporte de
uma linguagem compilada para binario foi eficiente para o desempenho comparado
com a em Elixir puro. Ainda que a utilizacao de NIFs possam acarretar em riscos
para a estabilidade da BEAM a geracao de dirty schedulers, como ja citado no
capıtulo 2.
Por fim, a implementacao usando a biblioteca Nx do Elixir apresentou um re-
sultado da mesma ordem de grandeza do Python com Tensorflow, algo esperado ja
que o Nx atualmente faz uso de um binding para Elixir do compilador algebrico do
Tensorflow, ou seja as duas implementacoes chamam o mesmo codigo compilado do
XLA.
22
Capıtulo 4
Conclusoes
O presente trabalho buscou, por meio do uso de diferentes linguagens, analisar e
comparar implementacoes do algoritmo KNN utilizando diferentes ferramentas para
um mesmo conjunto de dados.
Com base nos resultados obtidos no Capıtulo 3, foi observado, que ao recorrer a
bibliotecas numericas, o desempenho pode ser afetado em relacao a como o codigo
e estruturado. O loop em arrays, listas ou dicionarios do Python pode ser custoso,
assim, operacoes vetorizadas em bibliotecas numericas sao mapeadas para codigo
altamente otimizado, tornando-as muito mais rapidas do que as funcionalidades
padroes do Python. A utilizacao de Rust para criar funcoes otimizadas que podem
ser executadas em Elixir se provou eficiente para o cenario deste trabalho, apesar de
nao ser a solucao com melhor desempenho. O melhor teste para o Elixir foi obtido
pela biblioteca Nx, que apresentou um desempenho quase duas vezes maior que a
implementacao usando NIF. Apesar de recente, o Nx prove um futuro interessante
para a area de machine learning dentro da linguagem Elixir obtendo resultados da
mesma ordem de grandeza que os da biblioteca Tensorflow.
4.1 Trabalhos Futuros
Diante dos resultados obtidos, pode-se apontar como trabalhos futuros:
� Uma analise em diferentes cenarios da utilizacao de concorrencia em Elixir
proveniente da BEAM a fim de distribuir o algoritmo KNN e assim verifi-
23
car sua eficiencia neste cenario. Alem da possibilidade de clusterizacao da
maquina virtual, onde e possıvel ter o classificador executando na BEAM em
uma maquina de alto poder computacional, enquanto a coleta de dados e exe-
cutada em diversas maquinas com menor poder computacional.
� Testes com outros algoritmos de KNN alem do brute force: Os algoritmos Ball
Tree e KD Tree poderiam ser utilizados para analisar o comportamento das
ferramentos executando um algoritmo diferente.
� Utilizacao de GPU para atestar otimizacoes que sao realizadas pelo XLA e
EXLA. Apontar se no caso do algoritmo KNN o uso de GPU apresenta algum
grande ganho em desempenho.
24
Referencias Bibliograficas
[1] ALI, N., NEAGU, D., TRUNDLE, P., “Evaluation of k-nearest neighbour clas-
sifier performance for heterogeneous data sets”, SN Applied Sciences, v. 1, n. 12,
pp. 1–15, 2019.
[2] “Elixir”, https://elixir-lang.org, 2021, (Acesso em 10 Abril 2021).
[3] KURCWALD, K., “Eight Famous Companies Using Elixir?And Why They
Made the Switch”, https://www.monterail.com/blog/famous-companies-using-
elixir, 2021, (Acesso em 24 Maio 2021).
[4] SIM, S. E., EASTERBROOK, S., HOLT, R. C., “Using benchmarking to ad-
vance research: A challenge to software engineering”. In: 25th International
Conference on Software Engineering, 2003. Proceedings., pp. 74–83, IEEE,
2003.
[5] BHATT, S., “Python AI: Why Python is Better for Machine Lear-
ning and AI”, https://www.business2community.com/big-data/python-ai-why-
python-is-better-for-machine-learning-and-ai-02389380, 2021, (Acesso em 25
Maio 2021).
[6] “Rust”, https://www.rust-lang.org/, 2021, (Acesso em 10 Abril 2021).
[7] “elixir-nx/nx”, https://github.com/elixir-nx/nx, 2021, (Acesso em 25 Maio
2021).
[8] “numpy/numpy”, https://github.com/numpy/numpy, 2021, (Acesso em 25
Maio 2021).
[9] “tensorflow/tensorflow”, https://github.com/tensorflow/tensorflow, 2021,
(Acesso em 25 Maio 2021).
25
[10] JIAWEI HAN, MICHELINE KAMBER, J. P., Data Mining: Concepts and Te-
chniques, chapter Classification: Advanced Methods, Burnaby, Canada, Mor-
gan Kaufmann, pp. 423–425, 2005.
[11] ALGORITHM, K.-N. N. K., “Vaibhav Jayaswal”,
https://towardsdatascience.com/k-nearest-neighbors-knn-algorithm-
23832490e3f4, 2021, (Acesso em 17 Abril 2021).
[12] SIDHU, J., Natural Language Processing. A Machine Learning Approach to
Sense Tagged Words using K-Nearest Neighbor. M.Sc. dissertation, Punjab Uni-
versity, Julho 2018.
[13] HARRIGTON, P., Machine Learning in Action, chapter Classifying with k-
Nearest Neighbors, Connecticut, Manning, pp. 18–36, 2012.
[14] RASCHKA, S., “STAT 479: Machine Learning Lec-
ture Notes”, https://sebastianraschka.com/pdf/lecture-
notes/stat479fs18/02 knn notes.pdf, 2021, (Acesso em 17 Abril 2021).
[15] VANDERPLAS, J., “Benchmarking Nearest Neighbor Searches in Python”,
https://jakevdp.github.io/blog/2013/04/29/benchmarking-nearest-neighbor-
searches-in-python/, 2021, (Acesso em 24 Maio 2021).
[16] “What is NumPy?”, https://numpy.org/doc/stable/user/whatisnumpy.html,
2021, (Acesso em 10 Abril 2021).
[17] “NumPy - Array objects”, https://numpy.org/doc/stable/reference/arrays.html/,
2021, (Acesso em 17 Abril 2021).
[18] “Introduction to Tensors”, https://www.tensorflow.org/guide/tensor, 2021,
(Acesso em 10 Abril 2021).
[19] SAECKER, M., MARKL, V., Big Data Analytics on Modern Hardware Archi-
tectures: A Technology Survey, Berlin, Heidelberg, Springer Berlin Heidelberg,
pp. 125–149, 2013.
[20] “XLA: The TensorFlow compiler framework”,
https://haosdent.gitbooks.io/tensorflow-document/content/resources/xla prerelease.html,
2021, (Acesso em 17 Abril 2021).
26
[21] KEITH D. COOPER, L. T., Engineering a Compiler, chapter Intermediate
Representations, Houston, Morgan Kaufmann, pp. 221–234, 2003.
[22] “XLA Architecture”, https://www.tensorflow.org/xla/architecture, 2021,
(Acesso em 17 Abril 2021).
[23] LAVORINI, V., “Pure Python vs NumPy vs TensorFlow Performance Compa-
rison”, https://realpython.com/numpy-tensorflow-performance/, 2021, (Acesso
em 10 Abril 2021).
[24] CANDIDO, R., “Numpy VS Tensorflow: speed on Matrix calculati-
ons”, https://towardsdatascience.com/numpy-vs-tensorflow-speed-on-matrix-
calculations-9cbff6b3ce04, 2021, (Acesso em 10 Abril 2021).
[25] ADMIN, E., “Optimising for Concurrency: Comparing and contras-
ting the BEAM and JVM virtual machines”, https://www.erlang-
solutions.com/blog/optimising-for-concurrency-comparing-and-contrasting-
the-beam-and-jvm-virtual-machines/, 2021, (Acesso em 17 Abril 2021).
[26] “Actor Model”, https://enqueuezero.com/concepts/actor-model.html, 2021,
(Acesso em 24 Maio 2021).
[27] ALMEIDA, U., Learn Functional Programming with Elixir, chapter Thinking
Functionally, Brazil, The Pragmatic Programmers, pp. 1–9, 2018.
[28] “Java Platform, Standard Edition Java Virtual Machine Guide”,
https://docs.oracle.com/javase/9/vm/java-virtual-machine-technology-
overview.htm#JSJVM-GUID-982B244A-9B01-479A-8651-CB6475019281,
2021, (Acesso em 24 Maio 2021).
[29] JUCIMAR MAIA DA SILVA JR, RAFAEL DUEIRE LINS, L. M. D. S., “Com-
paring the performance of Java, Erlang and Scala in web 2.0 applications”,
IADIS International Journal on WWW/Internet, v. 10, pp. 121–137, 2013.
[30] FEDRECHESKI, G., COSTA, L. C. P., ZUFFO, M. K., “Elixir programming
language evaluation for IoT”. In: 2016 IEEE International Symposium on Con-
sumer Electronics (ISCE), pp. 105–106, 2016.
27
[31] “Erlang NIFs”, https://erlang.org/doc/tutorial/nif.html, 2021, (Acesso em 24
Maio 2021).
[32] “API functions for an Erlang NIF library”,
https://erlang.org/doc/man/erl nif.html, 2021, (Acesso em 24 Maio 2021).
[33] “The Rust Programming Language: Its History and Why It Mat-
ters”, https://www.talentopia.com/news/the-rust-programming-language-its-
history-and-why, 2021, (Acesso em 24 Maio 2021).
[34] CASTELLANI, M., Rust Concorrencia e alta performance com seguranca,
chapter Primeiros passos, Sao Paulo, Manning, pp. 1–19, 2018.
[35] KOCH, T., “Um panorama sobre Rust”, https://www.ime.usp.br/ gold/cur-
sos/2015/MAC5742/reports/rust.pdf, 2021, (Acesso em 18 Abril 2021).
[36] NOWACK, M., “Using Rust to Scale Elixir for 11 Million Concur-
rent Users”, https://blog.discord.com/using-rust-to-scale-elixir-for-11-million-
concurrent-users-c6f19fc029d3/, 2021, (Acesso em 10 Abril 2021).
[37] LIN, Y., BLACKBURN, S. M., HOSKING, A. L., et al., “Rust as a language for
high performance GC implementation”, ACM SIGPLAN Notices, v. 51, n. 11,
pp. 89–98, 2016.
[38] YAMAZAKI SUSUMU, MORI MASAKAZU, U. Y., HIDEKI, T., “Hastega:
A Method Using GPGPU for Super-Parallelization in Elixir Programming”,
Journal of Information Processing Society of Japan, v. 12, pp. 15–15, 2019.
[39] VALIM, J., “Nx (Numerical Elixir) is now publicly available”,
https://dashbit.co/blog/nx-numerical-elixir-is-now-publicly-available, 2021,
(Acesso em 10 Abril 2021).
[40] MORIARITY, S., “Nx Tip of the Week 1 - Using transforms”,
https://seanmoriarity.com/2021/02/16/nx-tip-of-the-week-1-using-
transforms/, 2021, (Acesso em 18 Abril 2021).
[41] MORIARITY, S., “Nx Tip of the Week 6 - Compiler or Bac-
kend?”, https://seanmoriarity.com/2021/03/25/nx-tip-of-the-week-6-compiler-
or-backend/, 2021, (Acesso em 18 Abril 2021).
28
[42] DAI, W., BERLEANT, D., “Benchmarking Contemporary Deep Learning
Hardware and Frameworks: A Survey of Qualitative Metrics”. In: 2019 IEEE
First International Conference on Cognitive Machine Intelligence, pp. 148–155,
2019.
[43] ARUOBA, S. B., FERNaNDEZ-VILLAVERDE, J., “A comparison of program-
ming languages in macroeconomics”, Journal of Economic Dynamics and Con-
trol, v. 58, pp. 265–273, 2015.
[44] “What is the Windows Subsystem for Linux?”, https://docs.microsoft.com/en-
us/windows/wsl/about, 2021, (Acesso em 24 Maio 2021).
[45] SOFEA, A., “For-loops vs. Matrix Multiplication”,
https://medium.com/@aishahsofea/for-loops-vs-matrix-multiplication-
ee67868f937, 2021, (Acesso em 24 Abril 2021).
[46] “Projeto Final”, https://github.com/carlosfrodrigues/projetofinal, 2021,
(Acesso em 25 Maio 2021).
[47] ULB, M. L. G., “Credit Card Fraud Detection”, https://www.kaggle.com/mlg-
ulb/creditcardfraud, 2021, (Acesso em 24 Abril 2021).
29