Original Article: Euphoria Performance Tips
Author: rapideuphoria.com
Euphoria Performance Tips

Dicas de desempenho da Euphoria


Dicas gerais

  • Se o seu programa for rápido o suficiente, esqueça-se de acelerá-lo. Basta torná-lo simples e legível.

  • Se o seu programa for muito lento, as dicas abaixo provavelmente não resolverão seu problema. Você deve encontrar um melhor algoritmo geral.

  • A maneira mais fácil de ganhar um pouco de velocidade é desligar a verificação de tipo de tempo de execução. Insira a linha:
               without type_check
    
    
    no topo do seu principal arquivo .ex, antes de quaisquer declarações de inclusão. Você normalmente ganhará entre 0 e 20 por cento dependendo dos tipos que você definiu, e os arquivos que você está incluindo. A maioria dos arquivos de arquivos padrão faz uma verificação de tipo definida pelo usuário. Um programa completamente sem verificação de tipo definida pelo usuário ainda pode ser acelerado ligeiramente.

    Além disso, tenha certeza de remover, ou comentar, qualquer

               with trace
    
               with profile
    
               with profile_time
    
    
    afirmações. with trace (mesmo sem chamadas para trace()), e with profile pode diminuir facilmente em 10% ou mais. with profile_time pode diminuir sua velocidade em 1%. Cada uma dessas opções também consumirá memória extra.

  • Os cálculos usando valores inteiros são mais rápidos do que os cálculos usando números de ponto flutuante

  • Declare as variáveis como inteiro, em vez de átomo, quando possível, e como seqüência em vez de objeto sempre que possível. Isso geralmente ganha alguns por cento em velocidade.

  • Em uma expressão envolvendo cálculos de ponto flutuante, geralmente é mais rápido escrever números constantes em forma de ponto flutuante, p. quando x tem um valor de ponto flutuante, diga, x = 9.9

    mude:
               x = x * 5
    
    
    para:
               x = x * 5.0
    
    
    Isso economiza o intérprete de ter que converter inteiro 5 em ponto flutuante 5.0 cada vez.

  • Euphoria faz uma avaliação curta de if, elsif, e while condições envolvendo and e or. A Euphoria deixará de avaliar qualquer condição, uma vez que determine se a condição é verdadeira ou não. Por exemplo, no if-statement:
            if x > 20 e y = 0 then
    
                ...
    
            end if
    
    
    O teste "y = 0" só será feito quando "x> 20" for verdadeira.

    Para a velocidade máxima, você pode solicitar seus testes. Faça "x> 20" primeiro se for mais provável que seja falso do que "y = 0".

    Em geral, com uma condição "A e B", a Euphoria não avaliará a expressão B, quando A é falso (zero). Da mesma forma, com uma condição como "A ou B", B não será avaliado quando A for verdadeira (diferente de zero).

    As afirmações if simples são altamente otimizadas. Com a versão atual do intérprete, simples aninhado se comparados os números inteiros geralmente são um pouco mais rápidos do que um único circuito curto, se for, por exemplo:

           if x > 20 then
    
               if y = 0 then
    
                   ...
    
               end if
    
           end if
    
    

  • A velocidade de acesso a variáveis particulares, variáveis locais e variáveis globais é a mesma.

  • Não existe uma penalidade de desempenho para a definição de constantes versus a conexão de números literais codificados. A velocidade de: para variáveis privadas, variáveis locais e variáveis globais é a mesma
               y = x * MAX
    
    
    é exatamente o mesmo que:
               y = x * 1000
    
    
    onde você definiu anteriormente:
               constant MAX = 1000
    
    
  • Não há penalidade de desempenho por ter muitos comentários em seu programa. Os comentários são completamente ignorados. Eles não são executados de forma alguma. Pode demorar alguns milissegundos por mais tempo para a carga inicial do seu programa, mas esse é um preço muito pequeno a pagar pela manutenção futura, e quando você ligar seu programa, ou traduzir seu programa para C, todos os comentários são removidos, então o custo se torna zero absoluto.


Medindo desempenho

Em qualquer linguagem de programação, e especialmente em Euphoria, você realmente precisa fazer medições antes de tirar conclusões sobre o desempenho.

A Euphoria fornece os dois perfil de contagem de execução, assim como perfil de tempo (DOS32 somente). Veja o refman.doc. Muitas vezes, você ficará surpreso com os resultados desses perfis. Concentre seus esforços nos lugares do seu programa que estão usando uma alta porcentagem do tempo total (ou pelo menos são executados um grande número de vezes). Não é necessário reescrever uma seção do código que usa 0,01% do tempo total. Normalmente, haverá um lugar, ou apenas alguns lugares onde o ajuste do código fará uma diferença significativa.

Você também pode medir a velocidade do código usando a função time(). por exemplo,

        atom t

        t = time()

        for i = 1 to 10000 do

            -- small chunk of code here

        end for

        ? time() - t

Você pode reescrever o pequeno fragmento de código de maneiras diferentes para ver qual é o caminho mais rápido.


Como acelerar Loops

Perfilamento irá mostrar-lhe os melhores pontos no seu programa. Estes são geralmente loops internos. Olhe para cada cálculo dentro do loop e pergunte-se se realmente precisa acontecer toda vez através do loop, ou poderia ser feito apenas uma vez, antes do loop.


Convertendo multiplicadores para adicionar em um loop

A adição é mais rápida do que a multiplicação. Às vezes, você pode substituir uma multiplicação pela variável de loop, com uma adição. Algo como:

        for i = 0 to 199 do

            poke(screen_memory+i*320, 0)

        end for

se torna:
        x = screen_memory

        for i = 0 to 199 do

            poke(x, 0)

            x = x + 320 

        end for


Salvando Resultados em Variáveis
  • É mais rápido salvar o resultado de um cálculo em uma variável, do que recalculá-lo mais tarde. Mesmo algo tão simples como uma operação de subscrito, ou adicionar 1 para uma variável vale a pena salvar.

  • Quando você tem uma seqüência com vários níveis de subscrição, é mais rápido mudar o código como:
               for i = 1 to 1000 do
    
                   y[a][i] = y[a][i]+1
    
               end for
    
    
    para:
               ya = y[a]
    
               for i = 1 to 1000 do
    
                   ya[i] = ya[i] + 1
    
               end for
    
               y[a] = ya
    
    
    Então você está fazendo 2 operações de subscrito por iteração do loop, em vez de 4. As operações, ya = y [a] e y [a] = ya são muito baratas. Eles apenas copiam um ponteiro. Eles não copiam uma seqüência completa.

  • Existe um custo leve quando você cria uma nova seqüência usando {a,b,c}. Se possível, mova esta operação para fora de um loop crítico armazenando-a em uma variável antes do loop e fazendo referência à variável dentro do loop.


In-lining of Routine Calls

Se você tem uma rotina bastante pequena e rápida, mas é chamada de muitas vezes, você economizará tempo fazendo a operação em linha, em vez de chamar a rotina. Seu código pode tornar-se menos legível, portanto, talvez seja melhor in-line apenas em locais que geram muitas chamadas para a rotina.


Operações em seqüências

A Euphoria permite que você opere em uma grande seqüência de dados usando uma única declaração. Isso evita que você escreva um loop onde você processa um elemento ao mesmo tempo. por exemplo.

        x = {1,3,5,7,9}

        y = {2,4,6,8,10}



        z = x + y

versos:
        z = repeat(0, 5)  -- if necessary

        for i = 1 to 5 do

            z[i] = x[i] + y[i]

        end for

Na maioria dos idiomas interpretados, é muito mais rápido processar toda uma seqüência (matriz) em uma declaração, do que é executar operações escalares em um loop. Isso ocorre porque o intérprete possui uma grande quantidade de despesas gerais para cada declaração que executa. A Euphoria é diferente. A Euphoria é muito magra, com pequena sobrecarga interpretativa, então as operações em seqüências nem sempre ganham. A única solução é o tempo das duas maneiras. O custo por elemento geralmente é menor quando você processa uma seqüência em uma declaração, mas há despesas gerais associadas com a alocação e desalocação de seqüências que podem inclinar a escala do outro lado.


Algumas otimizações de casos especiais

A Euphoria otima automaticamente certos casos especiais. x e y abaixo podem ser variáveis ou expressões arbitrárias.

        x + 1      -- faster than general x + y

        1 + x      -- faster than general y + x

        x * 2      -- faster than general x * y

        2 * x      -- faster than general y * x

        x / 2      -- faster than general x / y

        floor(x/y) -- where x and y are integers, is faster than x/y

        floor(x/2) -- faster than floor(x/y)

x abaixo é uma variável simples, y é qualquer variável ou expressão:

        x = append(x, y)   -- faster than general z = append(x, y)

        x = prepend(x, y)  -- faster than general z = prepend(x, y)



        x = x & y          -- where x is much larger than y,

                           -- is faster than general z = x & y

Quando você escreve um loop que "cresce" uma seqüência, anexando ou concatenando dados, o tempo, em geral, crescerá em proporção ao quadrado do número (N) dos elementos que você está adicionando. No entanto, se você pode usar uma das formas otimizadas especiais de anexar (), prepend () ou concatenação listadas acima, o tempo crescerá em proporção apenas a N (grosso modo). Isso poderia poupar um grande quantidade de tempo ao criar uma sequência extremamente longa. (Você também pode usar repeat () para estabelecer o tamanho máximo da seqüência e, em seguida, preencha os elementos em um loop, conforme discutido abaixo.)


Atribuição com Operadores

Para maior velocidade, converta:

        left-hand-side = left-hand-side op expression
para:
        left-hand-side op= expression
sempre que o lado esquerdo contém pelo menos 2 subíndices, ou pelo menos um subíndice e uma fatia. Em todos os casos mais simples, as duas formas funcionam à mesma velocidade (ou muito próximas da mesma).


Dicas de Pixel-Graphics

  • O modo 19 é o modo mais rápido para gráficos animados e jogos.

  • A memória de vídeo (no modo 19) não é armazenada em cache pela CPU. Geralmente, leva mais tempo para ler ou escrever dados na tela do que para uma área geral de memória que você aloca. Isso aumenta a eficiência de telas virtuais, onde você faz toda sua atualização de imagem em um bloco de memória que você pega do allocate(), e depois você periodicamente mem_copy() a imagem resultante para a real memória da tela. Desta forma, você nunca precisa ler a memória da tela (lenta).

  • Ao traçar pixels, você pode achar que os modos 257 e superiores são rápidos perto da parte superior da tela, mas lento perto da parte inferior.


Dicas de estilo de texto

Escrevendo texto na tela usando puts() ou printf() é bastante lento. Se necessário, em DOS32, você pode fazê-lo muito mais rápido, puxando para a memória de vídeo, ou usando display_text_image(). Há uma sobrecarga muito grande em cada um puts() para a tela, e um custo incremental relativamente pequeno por personagem. A sobrecarga com exw (WIN32) é especialmente alto (no mínimo no Windows 95/98 / ME). Linux e FreeBSD estão em algum lugar entre DOS32 e WIN32 na velocidade de saída de texto. Portanto, faz sentido construir uma string longa antes de colocar puts (), em vez de chamá-lo para cada personagem. Não há nenhuma vantagem para construir uma string com mais de uma linha no entanto.

A lentidão da saída de texto deve-se principalmente à sobrecarga do sistema operacional.


Rotinas de biblioteca

Algumas rotinas comuns são extremamente rápidas. Você provavelmente não poderia fazer o trabalho mais rápido de qualquer outra forma, mesmo se você usasse C ou linguagem de montagem. Alguns destes são:

  • mem_copy()
  • mem_set()
  • repeat()

Outras rotinas são razoavelmente rápidas, mas você pode ser capaz de fazer o trabalho mais rápido em alguns casos se a velocidade fosse crucial.

        x = repeat(0,100)

        for i = 1 to 100 do

            x[i] = i

        end for

é um pouco mais rápido do que:
        x = {}

        for i = 1 to 100 do

            x = append(x, i)

        end for

porque append() tem que alocar e redistribuir o espaço à medida que x cresce em tamanho. Com repeat(), O espaço para x é alocado uma vez no início. (append () é inteligente o suficiente para não alocar espaço com cada append para x. Ele alocará um pouco mais do que ele precisa, para reduzir o número de reafectações.)

Você pode substituir:

        remainder(x, p)

com:
        and_bits(x, p-1)

para maior velocidade quando p é uma potência positiva de 2. x deve ser um número inteiro não negativo que se encaixa em 32 bits.

arctan() is faster than arccos() or arcsin().


Procurando

O find() da Euphoria é a maneira mais rápida de procurar um valor em uma seqüência até cerca de 50 elementos. Além disso, você pode considerar uma hash table (demo\hash.ex) ou uma árvore binária (demo\tree.ex).


Ordenando

Na maioria dos casos, você pode usar a rotina do tipo de shell em include\sort.e.

Se você tiver uma grande quantidade de dados para classificar, você pode tentar um dos tipos em demo\allsorts.e (por exemplo, great sort). Se seus dados forem muito grandes para caber na memória, não confie na capacidade de troca automática de memória da Euphoria. Em vez disso, classifique alguns milhares de registros por vez e escreva-os para uma série de arquivos temporários. Em seguida, junte todos os arquivos temporários classificados em um grande arquivo ordenado.

Se os seus dados consistem apenas em números inteiros, e eles estão todos em um alcance bastante estreito, experimente o tipo de balde em demo\allsorts.e.


Aproveitando a memória cache

À medida que as velocidades da CPU aumentam, a diferença entre a velocidade da memória cache no chip e a velocidade da memória principal ou DRAM (memória dinâmica de acesso aleatório) torna-se cada vez maior. Você pode ter 256 Mb de DRAM no seu computador, mas o cache no chip provavelmente será apenas 8K (dados) mais 8K (instruções) em um Pentium, ou 16K (dados) mais 16K (instruções) em um Pentium com MMX ou um Pentium II / III. A maioria das máquinas também terá um cache de "nível 2" de 256K ou 512K.

Um algoritmo que passa por uma seqüência longa de alguns mil elementos ou mais, muitas vezes, do começo ao fim, realizando uma pequena operação em cada elemento, não usará o cache de dados no chip. Pode ser melhor passar uma vez, aplicando várias operações a cada elemento, antes de passar para o próximo elemento. O mesmo argumento é válido quando o seu programa começa a trocar, e os dados mais usados recentemente são movidos para o disco.

Esses efeitos de cache não são tão visíveis em Euphoria como eles são em linguagens compiladas de nível inferior, mas são mensuráveis.


Usando Machine Code e C

A Euphoria permite que você chame as rotinas escritas no código de máquina Intel de 32 bits. Em WIN32 e Linux e FreeBSD você pode chamar rotinas C em .dll ou .então arquivos, e essas rotinas C podem chamar suas rotinas Euphoria. Talvez seja necessário chamar C ou o código da máquina devido a algo que não pode ser feito diretamente na Euphoria, ou você pode fazê-lo para aumentar a velocidade.

Para aumentar a velocidade, o código da máquina ou a rotina C precisam fazer uma quantidade significativa de trabalho em cada chamada, caso contrário a sobrecarga de configurar os argumentos e fazer a chamada irá dominar o tempo, e pode não ganhar muito.

Muitos programas têm alguma operação de núcleo interno que consome a maior parte do tempo de CPU. Se você pode codificar isso em C ou código de máquina, deixando a maior parte do programa em Euphoria, você pode alcançar uma velocidade comparável a C, sem sacrificar a segurança e a flexibilidade da Euphoria.

 
Usando o tradutor Euphoria To C

Na versão 3.0, o Translator Euphoria To C completo está incluído no pacote de instalação. Ele irá traduzir qualquer programa Euphoria em um conjunto de arquivos de origem C que você pode compilar usando um compilador C.

O arquivo executável que você usa usando o Tradutor deve executar o mesmo, mas mais rápido do que quando você usa o intérprete. A aceleração pode ser de alguns por cento para um fator de 5 ou mais.