C++ X PHP X PYTHON X PERL X SHELL SCRIPT
Posted by Mathias Grimm on dezembro 23, 2008 in C/C++, PERL, PHP, Python, Shell Script
Bom dia.
Nesse artigo pretendo demonstrar um programa de conversão de csv para xml, escrito nas linguagens C++,PHP,PYTHON,PERL e SHELL SCRIPT. O Comparativo é basicamente em relação ao desempenho e a quantidade de linhas de código.
A idéia surgiu pois outro dia fui contratado (freela) pra fazer esse conversor, para um colega. Fiz inicialmente em Shell Script, pois foi a linguagem solicitada por esse colega, e o arquivo csv original tinha mais ou menos 2500 linhas, o que fica rápido em qualquer umas das linguagens que testei. Algum tempo depois resolvi fazer em python e depois pensei em fazer esse comparativo, pois a diferença foi brutal entre python e shell script.
O TESTE:
Tentei utilizar elementos comuns a todas linguagens, mas nem tudo ficou igual.
Já queria agradecer ao meu amigo e colega de trabalho, Vasco , que desenvolveu o código em C++.
Para calcular o tempo de execução utilizei o time, comando disponível para linux, que mede o tempo de execução de programas, desde o tempo de ativação até o fim de sua execução. Executei cada programa 5 vezes para obter o tempo médio de execução.
Nos testes utilizei um arquivo csv com 100000 linhas (cem mil linhas)
Segue abaixo o código dos programas.
C++:
#include <iostream> #include <sstream> #include <string> #include <time.h> #include <iomanip> using namespace std; FILE * fp; int i; string fields[9]; string intToStr(int number) { std::stringstream ss; std::string str; ss << number; ss >> str; return ss.str(); } void init() { fp = fopen("xml_from_c_plus_plus.xml", "w"); i = 1; } void _end() { fclose(fp); } void parseString(string line) { int f = 0; int pos = 0; size_t found; found=0; while(found != string::npos) { found = line.find("," , pos); fields[f++] = line.substr(pos, found - pos); pos = found+1; } } void xmlHeader() { string xmlString; xmlString = "\n \n"; fputs(xmlString.c_str(), fp); } void xmlFooter() { string xmlString; xmlString = ""; fputs(xmlString.c_str(), fp); } void xmlBody() { string xmlString; xmlString.append("\n"); xmlString.append(""); xmlString.append(fields[1]); xmlString.append("\n"); xmlString.append(""); xmlString.append(fields[0]); xmlString.append("\n"); xmlString.append(""); xmlString.append(fields[3]); xmlString.append("\n"); xmlString.append(""); xmlString.append(fields[5]); xmlString.append("\n"); xmlString.append(" "); xmlString.append(""); xmlString.append(fields[4]); xmlString.append("\n"); xmlString.append("\n"); xmlString.append("\n"); fputs(xmlString.c_str(), fp); i++; } void csv2xml() { xmlHeader(); FILE * fpo; char buffer[1024]; fpo = fopen("mathiasgrimm.csv", "r"); while(fgets(buffer, sizeof(buffer), fpo) != 0) { parseString(buffer); xmlBody(); } xmlFooter(); fclose(fpo); } int main( int argc, char ** argv ) { init(); csv2xml(); _end(); }
PYTHON:
def init(): global i global f f=open("xml_from_python.xml","w") i=1 def end(): f.close() def parseString(string): return string.split(",") def xmlHeader(): global f xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <prematricula centro=\"224\">\n" f.write(xmlString) def xmlFooter(): global f xmlString = "</prematricula>" f.write(xmlString) def xmlBody(fields): global i global f alumnoOpenNode = "%s%s%s"%(" <alumno id=\"ave_" ,i ,"\">\n" ) nombreNode = "%s%s%s"%(" <nombre>" ,fields[1],"</nombre\n" ) apelidoNode = "%s%s%s"%(" <apelido>" ,fields[0],"</apelido>\n" ) emailNode = "%s%s%s"%(" <email>" ,fields[3],"</email>\n" ) sexoNode = "%s%s%s"%(" <sexo>" ,fields[5],"</sexo>\n" ) licenciaOpenNode = " <licencia>\n" grupoNode = "%s%s%s"%(" <grupo>" ,fields[4],"</grupo>\n" ) licenciaCloseNode = " </licencia>\n" alumnoClseNode = " </alumno>\n" xmlString ="%s%s%s%s%s%s%s%s%s"%(alumnoOpenNode,nombreNode,apelidoNode,emailNode,sexoNode,licenciaOpenNode,grupoNode,licenciaCloseNode,alumnoClseNode) f.write(xmlString); i+=1 def csv2xml(): xmlHeader() for line in open('mathiasgrimm.csv').xreadlines(): fields = parseString(line) xmlBody(fields) xmlFooter() init() csv2xml() end()
PERL:
sub init { $i=1; } sub end { } sub parseString { } sub xmlHeader { <prematricula centro=\"224\">\n"; print f $xmlString; } sub xmlFooter { print f $xmlString; } sub xmlBody { $licenciaOpenNode = " <licencia>\n"; $licenciaCloseNode = " </licencia>\n"; $alumnoClseNode = " </alumno>\n"; $xmlString =sprintf("%s%s%s%s%s%s%s%s%s",$alumnoOpenNode,$nombreNode,$apelidoNode,$emailNode,$sexoNode,$licenciaOpenNode,$grupoNode,$licenciaCloseNode,$alumnoClseNode); print f $xmlString; $i++; } sub csv2xml { xmlHeader(); <mathiasgrimm.csv"); while ( ($linha = <csv>) ) { xmlBody(@fields); } xmlFooter(); } init(); csv2xml(); end();
PHP-CLI:
<?php function init() { global $i; global $f; $i=1; } function _end() { global $f; } function parseString($string) { } function xmlHeader() { global $f; $xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <prematricula centro=\"224\">\n"; } function xmlFooter() { global $f; $xmlString = "</prematricula>"; } function xmlBody($fields) { global $i; global $f; $licenciaOpenNode = " <licencia>\n"; $licenciaCloseNode = " </licencia>\n"; $alumnoClseNode = " </alumno>\n"; $xmlString =sprintf("%s%s%s%s%s%s%s%s%s",$alumnoOpenNode,$nombreNode,$apelidoNode,$emailNode,$sexoNode,$licenciaOpenNode,$grupoNode,$licenciaCloseNode,$alumnoClseNode); $i++; } function csv2xml() { xmlHeader(); { $fields = parseString($linha); xmlBody($fields); } xmlFooter(); } init(); csv2xml(); _end(); ?>
SHELL SCRIPT:
#!/bin/bash init() { f='xml_from_shell.xml' i=1 } parseString() { fields[0]=`echo $linha|awk -F, '{ print $1 }'` fields[1]=`echo $linha|awk -F, '{ print $2 }'` fields[2]=`echo $linha|awk -F, '{ print $3 }'` fields[3]=`echo $linha|awk -F, '{ print $4 }'` fields[4]=`echo $linha|awk -F, '{ print $5 }'` fields[5]=`echo $linha|awk -F, '{ print $6 }'` } xmlHeader() { echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $f echo " <prematricula centro=\"224\">" >> $f } xmlFooter() { xmlString="</prematricula>"; echo $xmlString >> $f } xmlBody() { echo " <alumno id=\"ave_$id\">" >> $f echo " <nombre>"${fields[1]}"</nombre>" >> $f echo " <apellido1>"${fields[0]}"</apellido1>" >> $f echo " <email>"${fields[3]}"</email>" >> $f echo " <sexo>"${fields[5]}"</sexo>" >> $f echo " <licencia>" >> $f echo " <grupo>"${fields[4]}"</grupo>" >> $f echo " </licencia>" >> $f echo " </alumno>" >> $f id=`expr $id + 1` } csv2xml() { xmlHeader while read linhaAtual do linha=$linhaAtual parseString xmlBody done < 'mathiasgrimm.csv' xmlFooter } init csv2xml
Bom... agora vou mostrar o tempo de execução de cada linguagem.
C++:

PYTHON:

PERL:

PHP-CLI:

SHELL SCRIPT:
Tempo de execução muito alto. Não foi possivel esperar o tempo necessário ao processamento de todo arquivo.
Realizei uma medição proporcional, deixei o script rodar por 2 minutos e utilizei o total de linhas processadas para esse cálculo.
CONCLUSÃO:
C++ foi o mais rápido, SHELL SCRIPT o mais lento
PYTHON foi o menor script em quantidade de linhas, C++ o maior

Perceba que python teve um diferença bem menor que as demais linguagens, e tem o código menor. Para esse conversor, especificamente, acredito que seria a melhor opção levando em conta a facilidade de escrever em python (o que pode variar de pessoa pra pessoa).
Mas essa avaliação deve ser feita individualmente para seu objetivo, ou seja, se seu objetivo é um código rápido, utilize c++, caso o objetivo seja escrever menos, independente da linguagem, utilize python.
O objetivo do artigo era mostrar algum comparativo entre as linguagens, mas isso poderia ser feito de muitas meneiras diferentes da utilizada no artigo.
Clique AQUI para baixar os arquivos utilizados nesse artigo.
Desculpe-me por algum equívoco ou falta de precisão.
Agradeço a visita de todos e voltem sempre!
10 Comentários on C++ X PHP X PYTHON X PERL X SHELL SCRIPT
By Jociel on dezembro 23, 2008 at 8:34 pm
Sugestão: inclua a linguagem Java no teste.
By MrBiTs on dezembro 24, 2008 at 5:40 am
Seu código em shell-script foi o mais longo dos 3 por causa da função parse_string. Você utilizou uma ferramenta externa ( awk ) para fazer um simples parse, que o IFS resolveria para você em 1/10 do tempo. Tente reesecrevê-la assim:
parseString() {
OLDIFS=${IFS}
IFS=,
set - ${linha}
fields[0] = $1
fields[1] = $2
fields[2] = $3
fields[3] = $4
fields[4] = $5
fields[6] = $6
IFS=${OLDIFS}
}
Vocẽ vai ver como o script ficará muito mais rápido. Trabalho arquivos com mais de 5 milhões de linhas dessa maneira, e não levo 10 minutos para processá-los.
By Mathias Grimm on dezembro 24, 2008 at 8:23 am
MrBits, essa maneira que tu informou realmente é mais rápida, mas mesmo assim ficou muito mais lenta do que as outras maneiras utilizadas. De qualquer forma já foi um ganho de performance muito significativo.
mathias@desenv:~/teste$ time ./csv2xml.sh
real 4m45.834s
user 2m48.711s
sys 1m59.791s
Levou tempo total de mais de 4 minutos enquanto c++ levou menos de 1 segundo, talvez fosse até interessante vc refazer seu script em outra linguagem.
Seu código está correto, apenas não existe esse espaço entre o “=” e os $1,$2,etc…
mas funcionou blz..
parseString()
{
OLDIFS=${IFS}
IFS=,
set - ${linha}
fields[0]=$1
fields[1]=$2
fields[2]=$3
fields[3]=$4
fields[4]=$5
fields[6]=$6
IFS=${OLDIFS}
}
Você conhece uma maneira mais otimizada de fazer aquele loop que fiz? Eu estava usando For linha in `cat arquivo.csv` mas é mais lenta do que a que estou utilizando no momento
Valeu ai pela observação!
Volte Sempre!
By MrBiTs on dezembro 24, 2008 at 5:43 pm
Correto. Não sei de onde eu tirei os espaços. O cat tende a ser mais lento do que um redirecionamento de STDIN, pelos motivos óbvios: abre-se um subshell para execução do cat.
cat arquivo | while read também não serve, pelo mesmo motivo: abrimos um subshell e ainda o “encanamos” ( pipeing ) para o while.
Temos aí duas propostas: você quer fazer scripts que utilizem recursos similares ou quer fazer scripts que utilizem o melhor das linguagens ? Se for a primeira opção, então os >> $f que você colocou para que o script escreva o arquivo destino linha a linha estão corretos e isso deixa o script ainda mais lento. Se a proposta é a segunda, então vamos tirar todos os >> $f e vamos fazer assim:
csv2xml()
{
xmlHeader > ${f}
while read linhaAtual
do
linha=$linhaAtual
parseString
xmlBody
done > ${f}
xmlFooter >> ${f}
}
Com essa modificação, obtive o seguinte tempo de processamento:
real 3m52.282s
user 2m23.564s
sys 2m33.173s
By MrBiTs on dezembro 24, 2008 at 5:55 pm
Falando agora de Python, a função xmlBody pode ficar mais “pythonica” se a escrevermos assim:
http://pastebin.com/f3cdae191
Veja que utilizei o recurso das “”" “”" para escrever o texto na formatação que eu quero.
By MrBiTs on dezembro 24, 2008 at 6:37 pm
E agora falando de Perl, eu considero muito mais dentro da filosofia da linguagem a utilização do split direto no código da função cvs2xml ao invés da função parseString, a não ser que você a tenha para passar além da string, o separador. Então, a coisa fica assim:
chomp($_);
my @fields = split(/\,/, $_);
O ideal seria você utilizar o módulo strict e inicializar as variáveis com o “my”. Além disso, use $| = 1; para que seja feito um flush a cada write.
A função xmlBody também pode ser escrita dentro dos mesmos conceitos aplicados ao Python. O código dela você vê aqui:
http://pastebin.com/f7ba936fb
By MrBiTs on dezembro 24, 2008 at 7:13 pm
E agora, para completar, refiz os scripts em bash, perl e python, e os postei em http://www.mrbits.com.br/tutoriais/comparativo.tar.gz. A quantidade de linhas ficou bem menor e eu fiz no que acredito ser o melhor de cada linguagem.
Em perl, baixei o tempo entre o meu e o seu script em 1s
Em python, o tempo permaneceu igual
Em bash, baixei o tempo de 3m para 1m. Aquele expr também estava matando seu script.
Veja que, mesmo usando built-ins do Bash, o script é realmente mais lento, da ordem dos poucos segundos para o minuto. Justifica-se. Embora você possa escrever um sistema completo somente em shell-script, a linguagem foi feita para auxiliar, não para performar.
By Mathias Grimm on dezembro 24, 2008 at 7:52 pm
Legal cara!
A maneira inicial que fiz, em shell script, quando fiz para meu amigo, foi parecida com a sua, sem funções e tal. Na verdade utilizei funções para facilitar o comparativo entre as linguagens… pra tudo ficar mais parecido.
Mas de qualquer forma já aprendi muitas coisas aí com oque vc falou.
Valeu!
By Mathias Grimm on dezembro 24, 2008 at 8:01 pm
De qualquer maneira, o ranking permanece igual… tendo uma alterações nas diferenças, não sendo suficientes para alterar a posição de nenhuma linguagem..
By Vinicius on abril 19, 2009 at 10:15 am
A sua versão em Shell Script lê cada linha 6 vezes na função parseString, fica realmente muito mais lento do que nas outras. O comparativo é válido mas o que você fez foi escrever o mesmo código em linguagens diferentes, não seria mais interessante usar o ‘jeito certo’ de cada linguagem pra resolver o mesmo problema? Talvez os resultados fossem outros.
Subscribe
Follow comments by subscribing to the C++ X PHP X PYTHON X PERL X SHELL SCRIPT Comments RSS feed.