CajuScript - Nova Versão! Um grande salto

O melhor esquema seria implementar uma interface com os operadores.

Sim, uma interface seria o melhor! Se quiser avançar com isso posso te ajudar no caminho das pedras.

Para pegar a source custumo usar o http://www.syntevo.com/smartsvn/download.html

A url do repositório é http://cajuscript.googlecode.com/svn/trunk/

Mas se prefirir eu te mando um e-mail com a source em zip… falamos melhor por mensagens privadas e e-mail :wink:

Olá.

Eu tenho problemas com desempenho do Groovy. No sistema que faço parte precisavos fazer importação de grandes arquivos de texto com formatos diferenciados. A solução que usamos foi utilizar grovvy para customização. Até aí tudo bem, mas notamos que 80% do tempo de importação é gasto com a execução do script. Quando vi o CajuScript informar ser até 50% mais rapido resolvi testar.

Com o teste que foi executado realmente o caju é mais rápido, porém quando aumentamos o numero de interações para 1 milhão e 10 milhões o groovy se demonstrou bem mais rápido. Chegou a ficar até 2 vezes mais rápido. Talvez seja necessário um teste mais preciso para avaliar qual dos dois seia mais rápido. Vou prosseguir nos testes realizando comparações, utilização de condicionais e arrays e postarei os resultados.

Abraços

[quote=gugarn]Olá.

Eu tenho problemas com desempenho do Groovy. No sistema que faço parte precisavos fazer importação de grandes arquivos de texto com formatos diferenciados. A solução que usamos foi utilizar grovvy para customização. Até aí tudo bem, mas notamos que 80% do tempo de importação é gasto com a execução do script. Quando vi o CajuScript informar ser até 50% mais rapido resolvi testar.

Com o teste que foi executado realmente o caju é mais rápido, porém quando aumentamos o numero de interações para 1 milhão e 10 milhões o groovy se demonstrou bem mais rápido. Chegou a ficar até 2 vezes mais rápido. Talvez seja necessário um teste mais preciso para avaliar qual dos dois seia mais rápido. Vou prosseguir nos testes realizando comparações, utilização de condicionais e arrays e postarei os resultados.

Abraços[/quote]

Olá Gugarn,

Exatamente, a situação real é a seguinte, CajuScript é muito rápido para iniciar, carregar o script e executar o script, mas quando tem muitos loops demorados por exemplo, como parece ser o teu caso, o CajuScript não consegue bater o Groovy.

Eu tenho noção das limitações do CajuScript comparado com o Rhino, existe apenas dois pontos que o Rhino é ainda mais rápido que o CajuScript, é ao criar novas instâncias de classes Java em Loops longos, e chamada de métodos com parâmetros em loops longos também. Invocação a métodos sem parâmetros em loops longos o CajuScript é mais rápido que o Rhino, mas em loops curtos o CajuScript sempre ganha, que é o que o teste que esta postado no site quer demonstrar.

O Rhino no desenvolvimento da versão 0.3 foi o ponto de referência, tentamos bater ele nos pontos que pudemos, e deixamos outros objetivos para as próximas versões, quanto a performance CajuScript vs Groovy nem faziamos muita idéia, como temos nos baseado somente no Rhino.

Bem com o CajuScript estamos o deixando apto para alta performance em ambiente Web, voltado para a framework que estamos desenvolvendo, e já usando em produção.

No nosso cenário os scripts são com loops curtos, é preciso criar várias instâncias do engine de script em multithreads e por isso tem que ser leve e o mais rápido possível neste cenário. E o CajuScript já alcansou este patamar de satisfação na versao 0.2, entretanto na versão 0.3 chega a ser 30% mais rápido no mínimo. Para o ambiente web creio que o CajuScript é o que consegue a melhor performance entre todos os outros, por que também estamos a faze-lo voltado para isto.

Agora quanto ao teu problema, se com o Groovy vc não esta tendo performance aceitável, talvez só em Java mesmo vc vai conseguir resultados mais satisfatórios, se vc puder e tiver tempo poderia me enviar por e-mail mais detalhes do teu problema? Para podermos investigar melhor e tentar futuramente melhorar o CajuScript neste aspecto também.

Para a versão 0.4 ou 0.5 temos como um dos desafios poder compilar scripts em bytecode, estamos pensando nisto e estudando a melhor abordagem, e queremos ter um protótipo já na versão 0.4, como o IronRuby e o IronPython fazem em .Net, gerando assemblies.

Muito obrigado pela chamada de atenção e pela sua partilha do problema. Não esqueça de se puder enviar mais detalhes, ok?

Valeu

[quote=Schuenemann][quote=onolox]
Assim me parece uma linguagem feita por brasileiros tentando ser americana.[/quote]
Não sabia que o único lugar do mundo onde se fala inglês é nos EUA. :roll:

Parabéns, eduveks. Ainda não tive oportunidade de testar, mas parece bem interessante.[/quote]

Desculpe uma resposta não muito voltada ao assunto.
Simplesmente porque sou mais um que achou este comentário absurdo.
Caro onolox, é lóóóóóógico que o normal é seguir um padrão.

Trocar try/catch por tentar/capturar seria a mesma coisa que o GUJ ter que trocar alí na barra lateral HOME por CASA ; Login por Logar e Logout por Deslogar.

finally { Pelo amor de Deus, que cabeça. ah nem. } :twisted:

Olá eduveks,

É um prazer poder compartilhar meu problema. Trabalho com softwares que importa dados de processos petroquimicos industriais (refino, transporte e explorção de petroleo). Então já imaginou a quantidade de dados gerado em um só dia né? Então cada dia de dados tem em média 100 mil registros em arquivo TEXTO para serem importados para a base de dados do sistema. Acontece que dependendo do ambiente industrial esse arquivo muda muito de padrão e em alguns casos simplesmente não há padrão!

Para que o software podesse se customizar as necessidades, sem ser preciso de uma recompilação e gerar nova versão do sistema, colocamos a funcionalidade de poder inserir um script para a interpretação do dado. Basicamente o script será executado para cada liha do arquivo. Existe vários passos a serem executados antes do script executar e basicamente o script lhe fornece a possibilidade de “entender” a linha lida. Então para cada linha do arquivo de texto é gerado um array de colunas que é enviada ao script para que o usuário possa fazer alguma computação necessária.

Então usamos funcionalidades simples do script como, por exemlpo:

if coluna[0] = “VLR” {

coluna[0] = "VALOR"

}

e por aí vai…

Então no funddo não executamos um longo scrtipt e sim um mesmo script curto várias vezes! Então precisamos recarregar o script o numero de linhas do arquivo. O groovy demora bastante pra realizar esta tarefa. Eu testei o CajuScript apenas aumentando a quantidade de loops do teste realizados por você, isso por que não tive ainda tempo de fazer um teste mais voltado para as nossas necessidades e comparar desempenho.

Amanhã irei fazer algumas simulações e postarei os resultados. De qualquer forma muitissimo obrigado pela ajuda e rapidez na resposta e só o fato de ter alguem realmente interessado em ajudar já me deixa bastante satisfeito.

E se o CajuScript se tornar vantajoso, com certeza adotaremos. Afinal o Grovvy realmente é bastante lento neste caso.

gugarn, se é o mesmo script varias vezes, e praticamente não tem loops, o CajuScript 0.3 com o sistema de cache deverá ser uma mão na roda pra ti.

Como expliquei anteriormente o CajuScript 0.3 com cache na pior das hipoteses chega a ser 30% mais rápido do que a versão 0.2.

Faz um teste assim:

for (int i = 0; i < 100; i++) { javax.script.ScriptEngine caju = new org.cajuscript.CajuScriptEngine(); caju.put("colunas", new String[] {"VLR"}); caju.eval("caju.cache test; array.get(colunas, 0) = 'VLR' ? array.set(colunas, 0, 'VALOR'); ?;"); System.out.println(((String[])caju.get("colunas"))[0]); }

E compara a mesma situação com o Groovy.

Deve ser um teste mais próximo do teu cenário.

Usando o sistema de cache, se o script é sempre o mesmo, vai ficar bala, para cada script tem q ter um ID do cache diferente, pois é este ID que identifica qual é o cache a ser usado.

caju.cache: ID_DO_CACHE;

Os ":" tanto faz, pode ser também:

caju.cache - ID_DO_CACHE;

Etc… o que interessa é o espaço antes de começar a ID do cache.

Tenta assim então e depois diz ai o resultado :wink:

Fiz aqui este teste:

        long time;
        time = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            javax.script.ScriptEngine caju = new org.cajuscript.CajuScriptEngine();
            caju.put("colunas", new String[] {"VLR"});
            caju.eval("caju.cache test; array.get(colunas, 0) = 'VLR' ? array.set(colunas, 0, 'VALOR'); ?;");
        }
        System.out.println("CajuScript: "+ (System.currentTimeMillis() - time) + "ms");
        time = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            groovy.lang.GroovyShell groovyShell = new groovy.lang.GroovyShell();
            groovyShell.setVariable("colunas", new String[] {"VLR"});
            groovyShell.evaluate("if (colunas[0].equals(new String(\"VLR\"))) {\n colunas[0] = \"VALOR\"\n }\n");
        }
        System.out.println("Groovy: "+ (System.currentTimeMillis() - time) + "ms");

Resultado:

E outro iniciando os engines fora do loop:

        long time;
        time = System.currentTimeMillis();
        javax.script.ScriptEngine caju = new org.cajuscript.CajuScriptEngine();
        for (int i = 0; i < 1000; i++) {
            caju.put("colunas", new String[] {"VLR"});
            caju.eval("caju.cache test; array.get(colunas, 0) = 'VLR' ? array.set(colunas, 0, 'VALOR'); ?;");
        }
        System.out.println("CajuScript: "+ (System.currentTimeMillis() - time) + "ms");
        time = System.currentTimeMillis();
        groovy.lang.GroovyShell groovyShell = new groovy.lang.GroovyShell();
        for (int i = 0; i < 1000; i++) {
            groovyShell.setVariable("colunas", new String[] {"VLR"});
            groovyShell.evaluate("if (colunas[0].equals(new String(\"VLR\"))) {\n colunas[0] = \"VALOR\"\n }\n");
        }
        System.out.println("Groovy: "+ (System.currentTimeMillis() - time) + "ms");

Resultado:

Teste com o Groovy 1.5.7 e CajuScript 0.3.

Não sou um expert em Groovy, muito pelo contrário, levei mó tempão para descobrir como fazer o IF com strings, não sei se o script do Groovy esta da melhor maneira, mas não consegui fazer de outro jeito mais simples, mas acho que não deve influenciar nos tempos.

Estes tempos do Groovy me assustam, isto esta correto? Tem alguma coisa errada ai?

Olá pessoal,

Fiz mais alguns testes com os scripts e tenho alguns resultados a postar para discurssões.
Nos testei usei um Pentium 4 com HyperThread 3ghz com 2Gb de memória e durante este período apenas a aplicação do teste estava sendo executada. Apesar do HyperThread, a JVM só usou 50% do processamento, isso por que ela não aloca automaticamente o outro processador, mas enfim.

A situação do teste foi semelhante a uma situação sem loops porém com uso de testes condicionais. Isso por que para minha aplicação é o que deverei utilizar ao contrário de scripts muito longos com loops longos.

  • O eixo X contém o número de interações. Fiz o teste até 100mil interações.

  • O eixo Y contém o o tempo em milissegundos gasto.

      [img]http://www.dca.ufrn.br/~guga/testeScripts_files/image003.png[/img]
    

Gostaria de comentar o consumo de memória. Para este teste o LUA chegou a consumir 63MB, o Groovy 160MB e o Caju, Incríveis 13MB. O gerenciamento do Caju me pareceu bastante bom, o LuaJava tem problemas realmente no gerenciamento de sua memória, pois o consumo aumenta linearmente com o numero de interações ( para um milhão de interações ele chegou a consumir 1GB ). Em contato com os desenvolvedores do LUA eles assumiram a falha e deverá ser corrigida na próxima versão. Diferentemente, o CajuScript se manteve estável independente do número de interações processadas.

Para um script que roda 100% dentro da plataforma JAVA o CajuScript tem um desempenho MUITO BOM. O LUA é realmente bem mais rápido, mas precisa de uma DLL para funcionar.

O Groovy se demostrou muito pesado e o gerenciamento da memória não me pareceu satisfatório, vou postar o código fonte e se alguem souber uma forma de melhorar este desempenho que poste seus comentários aqui. O Groovy me pareceu bom quando o script tem bastante loops demorados, mas para aplicações que precisam processar muitas vezes com curtos scripts ele não se demonstrou adequado.

O LUA não trata ainda array nativos do JAVA então fui obrigado a por o Array dentro do ArrayList e passar o objeto inteiro, porém isso deixou o LUA em desvantagem pois contabilizei este tempo como processamento do script. Mesmo assim o LUA por ser executado diretamente por uma DLL tem o desempenho muito bom.

Código fonte Utilizado para teste:


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package testecajuscript;

import java.util.ArrayList;
import javax.script.ScriptException;
import org.keplerproject.luajava.LuaState;

/**
 *
 * @author user
 */
public class ScriptsTester {

    public static void main(String[] args) {
        try {
            System.out.println("======================");
            System.out.println("Scripts Tester");
            System.out.println("======================");
            //testeLua(1000000);
            //testeCaju(100000);
            testeGroovy(100000);

        } catch (Exception e) {
            throw new Error(e);
        }
    }

    public static void testeCaju(int interacoes) throws ScriptException {

        long time = 0;

        String colunas[] = {"ZZ", "XX", "CC", "VV", "TT", "YY", "UU", "QQ", "WW", "EE", "RR"};

        String scriptCaju = "caju.cache test;";
        scriptCaju += "array.get(colunas, 0) = 'ZZ' ? array.set(colunas, 0, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 1) = 'XX' ? array.set(colunas, 1, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 2) = 'CC' ? array.set(colunas, 2, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 3) = 'VV' ? array.set(colunas, 3, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 4) = 'TT' ? array.set(colunas, 4, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 5) = 'YY' ? array.set(colunas, 5, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 6) = 'UU' ? array.set(colunas, 6, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 7) = 'QQ' ? array.set(colunas, 7, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 8) = 'WW' ? array.set(colunas, 8, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 9) = 'EE' ? array.set(colunas, 9, 'VALOR'); ?;";
        scriptCaju += "array.get(colunas, 10) = 'RR' ? array.set(colunas, 10, 'VALOR'); ?;";

        /*  Teste CajuScript */

        System.out.println("\n---------------- CajuScript ------------------");

        time = System.currentTimeMillis();
        javax.script.ScriptEngine caju = new org.cajuscript.CajuScriptEngine();
        for (int i = 0; i < interacoes; i++) {
            
            caju.put("colunas", colunas);
            caju.eval(scriptCaju);

            if (i == 1 || i == 100 || i == 1000 || (i >= 10000 && (i % 10000) == 0)) {
                System.out.println("Interações: " + i + " Tempo: " + (System.currentTimeMillis() - time) + "ms");
            }            

        }

        System.out.println("Interações Totais: " + interacoes + " Tempo Total: " + (System.currentTimeMillis() - time) + "ms ");


    }

    public static void testeGroovy(int interacoes) {

        long time = 0;

        String colunas[] = {"ZZ", "XX", "CC", "VV", "TT", "YY", "UU", "QQ", "WW", "EE", "RR"};

        String scriptGroovy = "(colunas[0] == 'ZZ' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[1] == 'XX' ? 'VALOR' :  ''); ";
        scriptGroovy += "(colunas[2] == 'CC' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[3] == 'VV' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[4] == 'TT' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[5] == 'YY' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[6] == 'UU' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[7] == 'QQ' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[8] == 'WW' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[9] == 'EE' ? 'VALOR' : ''); ";
        scriptGroovy += "(colunas[10] == 'RR' ? 'VALOR' : ''); ";

        System.out.println("\n---------------- Groovy ------------------");

        time = System.currentTimeMillis();
        
        for (int i = 0; i < interacoes; i++) {
            groovy.lang.GroovyShell groovyShell = new groovy.lang.GroovyShell();    
            groovyShell.setVariable("colunas", colunas);
            groovyShell.evaluate(scriptGroovy);

            if (i == 1 || i == 100 || i == 1000 || (i >= 10000 && (i % 10000) == 0)) {
                System.out.println("Interações: " + i + " Tempo: " + (System.currentTimeMillis() - time) + "ms");
            }

        }
        System.out.println("Interações Totais: " + interacoes + " Tempo Total: " + (System.currentTimeMillis() - time) + "ms ");


    }

    public static void testeLua(int interacoes) {

        long time = 0;

        String colunas[] = {"ZZ", "XX", "CC", "VV", "TT", "YY", "UU", "QQ", "WW", "EE", "RR"};

        String scriptLua = "if array:get(0) == 'XX' then array:set(0,'VALOR') end";
        scriptLua += "if array:get(1) == 'XX' then array:set(1,'VALOR') end";
        scriptLua += "if array:get(2) == 'XX' then array:set(2,'VALOR') end";
        scriptLua += "if array:get(3) == 'XX' then array:set(3,'VALOR') end";
        scriptLua += "if array:get(4) == 'XX' then array:set(4,'VALOR') end";
        scriptLua += "if array:get(5) == 'XX' then array:set(5,'VALOR') end";
        scriptLua += "if array:get(6) == 'XX' then array:set(6,'VALOR') end";
        scriptLua += "if array:get(7) == 'XX' then array:set(7,'VALOR') end";
        scriptLua += "if array:get(8) == 'XX' then array:set(8,'VALOR') end";
        scriptLua += "if array:get(9) == 'XX' then array:set(9,'VALOR') end";
        scriptLua += "if array:get(10) == 'XX' then array:set(10,'VALOR') end";

        /*  Teste LuaScript */

        System.out.println("\n---------------- LuaJava ------------------");



        ArrayList array;
        LuaState L;

        time = System.currentTimeMillis();



        array = new ArrayList();

        for (int j = 0; j < colunas.length; j++) {
            array.add(colunas[j]);
        }


        L = org.keplerproject.luajava.LuaStateFactory.newLuaState();
        L.openLibs();

        for (int i = 0; i < interacoes; i++) {
            
            L.pushJavaObject(array);
            L.setGlobal("array");
            L.LdoString(scriptLua);

            if (i == 1 || i == 100 || i == 1000 || (i >= 10000 && (i % 10000) == 0)) {
                System.out.println("Interações: " + i + " Tempo: " + (System.currentTimeMillis() - time) + "ms");
            }

            L.pop(1);

        }


        System.out.println("Interações Totais: " + interacoes + " Tempo Total: " + (System.currentTimeMillis() - time) + "ms ");

    }
}

Muito obrigado Gugarn, muito bom mesmo, excelente trabalho!

Estou estremamente contente com este resultado, realmente tive muito trabalho para afinar a versão 0.3 e mais uma vez confirmo que valeu a pena.

Quanto ao LuaJava, eu implementei as interfaces de scripting do java 6 para o Lua, falava diretamente com o Thiago Ponte, eu usava LuaJava, mas me deparei com varios problemas, o principal foi problemas em multithreads, o LuaJava deixa threads penduradas, usar em ambiente web se tornou impraticável, eu mesmo assim insisti durante muito tempo no LuaJava, e também tem um bug com concatenação de strings longas, por exemplo fazer longas querys em string no LuaJava, simplismente a string ficava estragada e tinha que fazer concatenação de strings mais curtas para contornar o problema. E eu falei com o Thiago sobre estes problemas todos ele disse que tinha uma nova versão pronta para lançar e talz, mas isto ja faz um ano, e até agora nada da tal versão, e passou alguns meses e ai resolvi começar com o CajuScript.

Pois é fiz o CajuScript mesmo para substituir o LuaJava naquilo que eu precisava, ser simples como o LuaJava, e ter boa performance.

O LuaJava é excelente, até inicialmente pensei em fazer um interpretador do Lua 100% Java, mas depois comecei a ter idéias de sintaxes, e surgio a idéia de fazer a sintaxe dinâmica, depois também teria o problema de manter a compatibilidade com o Lua.

Quando lancei a primeira versão do CajuScript enviei um email para a equipe do LuaJava falando do projeto, e dizendo que continuaria a ajudar o LuaJava no que fosse preciso.

Mas sinceramente sinto que o projeto do LuaJava esta meio parado, pasado 1 ano continua na mesma. O Thiago tem os seus motivos, mas pra mim passou a ser complicado apostar num motor com bugs cruciais e muito tempo sem resposta prática. Mas espero que haja o quanto antes uma nova versão do LuaJava.

Se vc escolher usar o LuaJava eu posso te dar um apoio também em como contornar alguns problemas, por exemplo para contornar o problema das threads e memória pendurada, passei a executar os scripts num novo processo Java, assim depois do script executado era só matar o processo, mas tinha um consumo de tempo alto para iniciar, mas podes iniciar o novo processo, executar os scripts necessarios e depois matar o processo. Fazia isto usando RMI tb, e foi baseado nisto que fiz a implementação do LuaJava para a API de Scripting do Java 6.

Mais uma vez, gostei muito dos testes que fizeste, obrigado mesmo, se gostas mesmo destas aventuras com motores de script, e se quiser entrar para o projeto do CajuScript fica aqui o convite.

Qualquer sugestão ou critica será muito bem vinda. E vamos continuar trabalhando para melhorar ainda mais o CajuScript.

Valeu :wink:

gugarn, olhando rapidamente o teu codigo de teste reparei em alguns problemas.

O script do lua não esta bem, os ifs, só faz o primeiro IF, mas isto não influencia o resultado por que, os 3 engines só devem ter entrado dentro do IF uma única vez, na primeira vez, pois alteram o valor no Array e depois nas outras iterações fizeram apenas o if e o get, e não fizeram mais o set.

Entendeu? Por isso que no teste que eu postei, eu criei o array dentro do loop, para garantir que sempre entraria dentro do IF.

Por que vc dentro do script esta alterando a referencia do objeto do array, por isso o array fora também é alterado.

Mas isto não altera o resultado do teste, por que os 3 scripts não fizeram o set mais que 1 vez, e todos tiveram o mesmo comportamento.

Faz dentro dos loops um:

System.out.println(colunas[0]);

E vc vai ver que o array fora vai estar com a alteração feita no script.

Por exemplo este código:

String[] colunas = new String[] {"VLR"}; for (int i = 0; i < 10; i++) { javax.script.ScriptEngine caju = new org.cajuscript.CajuScriptEngine(); caju.put("colunas", colunas); System.out.println(colunas[0]); caju.eval("caju.cache test; array.get(colunas, 0) = 'VLR' ? array.set(colunas, 0, 'VALOR'); ?;"); System.out.println(colunas[0]); }

O output vai ser:

Ou seja só entrou dentro do IF na primeira vez.

Mas como já disse isto não altera o resultado, pois todos se comportaram da mesma forma :wink:

de qualquer forma vou refazer e atualizar os resultados. =)