Executando verificação de segurança...
18

Tentando mudar o PHP para melhor com C

Fala pessoal,

Meu nome é Henrique e queria falar um pouco sobre um projeto que estou trabalhando e muito animado com o retorno da comunidade PHP no Reddit até agora. Conheci hoje esse projeto incrível que o Rafael criou e pensei em compartilhar com a comunidade dev brasileira.

Minha relação com PHP

Eu comecei a programar com PASCAL quando tinha 10 anos, e aos 12 pra 13 anos conheci o C. Mais tarde, arranjei meu primeiro emprego como desenvolvedor PHP aos 17, de lá pra cá (tenho 31) passei por Java, Javascript, Python e hoje trabalho mais com Python e .NET Core. Porém, sempre acompanhei a linguagem PHP e os projetos em torno dela (Laravel, etc) por ser uma linguagem que gosto e levo de coração por me dar meu primeiro emprego haha.

O problema do PHP 8

Alguns anos atrás me deparei com esse problema em relação a performance do PHP para executar cálculos matemáticos em larga escala, principalmente álgebra linear, aritmética em grande quantidade de números e afins.

As arrays do PHP são excelentes como arrays genéricas aonde você pode armazenar tudo, mas são péssimas quando você precisa iterar para acumular, somar e acessar valores. Para os casos aonde você só precisa iterar 100 vezes, ótimo, você provavelmente vai achar que estou falando bobagem. Mas, se você em que carregar 1 milhão de linhas de um arquivo pra fazer uma soma/divisão/multiplicação igual em todas elas, você vai ver que a demora pode começar a ficar inaceitável.

Isso acontece porque as arrays do PHP nada mais são que HashTables no C, ou seja, para cada associação de chave valor você tem um algoritmo de hashing para encontrar seus índices lá no C. Isso torna ela bem prática de usar já que você pode gerar chaves até com strings, porém, torna ela bem lenta por conta do algoritmo de hashing.

Esse problema afeta também o PHP de ser efetivo em outros problemas como:

  • Machine Learning e afins
  • Visão computacional e processamento de imagens
  • Sistemas financeiros ou produtos que o core trabalha com matemática em larga escala

O PHP tem até uma ótima biblioteca de Machine Learning chamada RubixML, mas infelizmente para a maior parte dos casos a lentidão é um impeditivo para de fato implementar os algoritmos em produção e larga escala.

Consertando a array do PHP criando um novo tipo de array especializada em números

Pensando nessa brecha que existia no ecosistema do PHP, decidi encarar o desafio e criei uma extensão para o PHP em C que cria uma nova array chamada NDArray.

use \NDArray as nd;

$a = nd::array([[1, 2], [3, 4]]);
print_r($a);
[[1, 2]
 [3, 4]]

No código acima a gente criou uma NDArray através de uma array do PHP. Agora podemos tratar $a como um array normal. Você pode usar foreach e acessar as linhas e colunas usando o índice que você usaria em um array do PHP ($a[1]).

Porém, esse é um array especial que te oferece uma lista considerável de operações matemáticas especializadas. Por exemplo, se eu quiser multiplicar todos os valores de $a por 2, basta eu escrever:

$b = $a * 2;
print_r($b)'
[[2, 4]
 [6, 8]]

Não só é uma forma mais prática de fazer isso (ao invés de um bando de foreachs) como é em média 10 vezes mais rápido que a mesma coisa usando somente o PHP.

Existem dezenas de operações de estatística, lógica, aritmética, manipulação, álgebra linear e afins. Se você quiser o produto da matriz $a pela matriz $b:

$produto = nd::matmul($a, $b);

A biblioteca foi propositalmente construída para lembrar as funções de mesmo nome do NumPy no Python.

Você pode selecionar colunas e manipular sua array com muito mais facilidade:

$slice = $a->slice([], [1, 2]); // [] significa todas as linhas, [1, 2] significa coluna de 1 a 2 com step 1, ou seja, apenas a segunda coluna
print_r($slice);
[[2]
 [4]]

E depois que terminar tudo pode transformar em um array do PHP chamando o método toArray:

$array_php = $a->toArray();

Suporte para GPU NVIDIA (CUDA)

Construi a biblioteca também para ser compátivel com placas de vídeo NVIDIA. Para realizar operações na GPU, basta executar o código extremamente complexo abaixo:

$a_gpu = $a->gpu();
$b_gpu = $b->gpu();

$a_x_b = $a_gpu * $b_gpu;

No código acima copiamos nossas duas arrays para a GPU e multiplicamos elas. A multiplicação é totalmente executada pela GPU e $a_x_b também estará na GPU. 95% das operações da biblioteca são compátiveis com a GPU e possuem kernels CUDA customizados.

Pra copiar a array de volta para a CPU, mais um código complexo:

$a = $a_gpu->cpu();

Suporte para imagens GD

Para manipulação de imagens, uma imagem GD é compátivel com a NDArray:

$img = imgcreatefromjpg("test_img.jpg");
$img_array = nd::array($img);

// Operações na imagem aqui...

$img = $img_array->toImage(); // Converte a NDArray para GDImage novamente

Agora temos um tensor de tamanho (3, h, w) sendo h a altura da imagem, w a largura da imagem e 3 os 3 canais RGB.

Reações e o futuro

Acredito que isso poderá influenciar muitas áreas no PHP, algumas pessoas no reddit pareciam quase que aliviadas e isso me deu muito ânimo. Lancei no sabádo e cada estrelinha lá no Github me deu ainda mais ânimo pra continuar.

Estou trabalhando para a release estável e em uma biblioteca de processamento de imagem super rápida e com suporte para GPU utilizando as NDArray. Eu acho que as bibliotecas GD e ImageMagick são cansativas demais, então pensei em criar algo usando essa tecnologia.

O criador da biblioteca RubixML, Andrew Dalpino, entrou em contato comigo e disse que pretende sugerir a comunidade do Rubix que adote a minha extensão como array padrão para a próxima versão. Isso vai ajudar muito no crescimento do ecosistema no PHP e a democratizar o acesso a machine learning que atualmente tem sido comandado pelo Python exatamente por possuir esses recursos e um ecosistema sólido ao redor dele.

Lançamento do Preview no Reddit: https://www.reddit.com/r/PHP/comments/156doyf/i_created_a_better_array_for_mathematical/
Site: https://numpower.org
GitHub: https://github.com/NumPower/numpower
DockerHub: https://hub.docker.com/r/numpower/numpower

Carregando publicação patrocinada...
2
1
2

Essa não é uma biblioteca que resolve um problema de ML especificamente, bibliotecas da Machine Learning podem usá-la. Muitos feedbacks que recebi são de pessoas que precisavam apenas de algo mais eficiente para lidar com grandes quantidades de números e matemática repetida em grandes datasets.

Outro caso de uso especificamente é para manipulação de imagens que fica bem mais eficiente. Isso tudo sem precisar de adicionar um serviço em outra linguagem ou aumentar sua stack (tem muita gente por ai que não tem interesse em ter um produto com 3 linguagens).

O RubixML mesmo tem uma comunidade bem ativa e casos de uso em produção de pessoas que simplesmente não queriam introduzir Python em seus produtos somente para fazer uma predição em um modelo Naive Bayes, e podem fazê-lo dentro de seus próprios backend Laravel, Symfony e afins.

Outro potencial é abrir seus modelos treinados no Python mas usar o PHP para realizar a inferência/predição no seu website em produção, evitando utilizar mais um serviço.

Fora isso, a minha biblioteca usa float32, diferentemente do PHP que usa double precision para tudo. O PHP gasta o dobro de memória para armazenar a mesma quantidade de números, então só de usar a NDArray você já economiza memória a troco de uma precisão que para a maior parte dos casos é inútil.

1

Sim, na parte do númerico havia entendido por isso achei bem interessante a solução pra otimizar isso. No próprio python a gente acapa usando numpy as vezes pra otimizar já que a linguagem por si só é lenta.

Show de bola, obrigado pelo esclarecimento.

1

Isso mesmo, eu criei ela exatamente porque achei que o PHP merecia algo como o NumPy haha (com essa pimenta extra da GPU que decidi fazer como um desafio).

Falo isso como um fã e usuário do NumPy, essa lib é uma homenagem haha. E foi lendo o código-fonte C do NumPy que descobri como fazer uma implementação eficiente.

1

Bem legal, eu sinto falta no PHP suporte a collections que usam B-tree e outros tipos de organização. Talvez tenha libs que façam isso, mas se fizer nativo, é muito melhor. Parabéns.

1

cara ! genial realmente esta lib vai ser muito util ! eu estava preferindo realizar novas consultas ao banco de dados doque interar em cima de um cache como collection devido ao tempo de execução.

1

Show de bola. Eu ia perguntar até que ponto esse "Numpy4PHP" vai, mas eu mesmo vou me dar uma resposta utilizando em um problema da física, onde o numpy mesmo é bem lento. Caso queira responder, fique a vontade

Em vários problemas físicos, você tem matrizes e precisar percorrer todas a matriz e fazer operações (como você me descreveu). No caso em que eu tenho, onde numpy é lento e sua aplicação é baseado no código C/C++ o PHP seria mais rápido? Por exemplo, em problemas para solucionar a equação da onda, temos que fazer operações sobre a matriz (Laplaciano). O jeito de fazer o Python ser rápido é usar Numba (JIT), usando loop de loop para percorrer a matriz, ao invés de usar vetorização (slice) da matrix. Esse jeito "vanilla" acelera bastante, até deixando mais rápido com uma implementação simples feita em C.

O PHP usa JIT também (acho que na versão 8) e fica a pergunta. Nesse meu caso, onde o loop de loop é mais rápido (por conta do JIT) do que vetorização, acontece nessa sua implementação? Em outras linguagens, como Julia, esse jeito "vanilla" de loop de loop é também mais rápido (Julia também usa JIT), e Julia é uma das linguagens mais rápida existentes.

0