ADD vs TAKE - tempo de execução em filas bloqueantes

6 respostas
ops.rio

Olá pessoal,

seguinte estou com uma dúvida e queria saber se alguém aqui já passou por isso segue o código da classe de teste abaixo,

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

import java.util.concurrent.PriorityBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author 
 */
public class TesteFila {
    private final int LIMIT = 500000;
    private PriorityBlockingQueue<Integer> queue;
    //private LinkedBlockingQueue<Integer> queue;
    //private ArrayBlockingQueue<Integer> queue;
    
    public static void main(String[] args) {
        TesteFila testeFila = new TesteFila();
        testeFila.start();
    }
    
    public void start(){
        queue = new PriorityBlockingQueue<Integer>(LIMIT);
        TH_Consumer tc = new TH_Consumer();
        new Thread(tc).start();
        test();
        //tc.stop();
    }
    
    public class TH_Consumer implements Runnable{
        private boolean running = true;
        private long t;
        private long total = 0;
        public void run(){
            try {
                int count = 0;
                long t = 0l;
                while(count < LIMIT){
                    t = System.nanoTime();
                    queue.take();
                    total += (System.nanoTime() - t);
                    count++;
                }
                System.out.println("Total Take: " + count);
                System.out.println("Total Tempo: " + total);
            } catch (Exception ex) {
                Logger.getLogger(TesteFila.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        public void stop(){
            this.running = false;
        }
    }
    
    public void test(){
        long t = 0l;
        int i = 0;
        long total = 0;
        while(i < LIMIT){
            t = System.nanoTime();
            queue.add(i);
            total += (System.nanoTime() - t);
            i++;
        }
        System.out.println("total add: " + i);
        System.out.println("total tempo: " + total);
    }
    
}

Qual o problema?
O problema é o seguinte, ao medir o tempo que leva o add na fila e o take da fila percebi, diferentemente do que eu pensava, que o take leva mais tempo do que o add e no meu cenário atual de uma média de 51 mensagens por milliseconds as mensagens acumulam, me gerando problemas.

o teste foi realizando entre as seguintes filas

PriorityBlockingQueue<Integer>
LinkedBlockingQueue<Integer>
ArrayBlockingQueue<Integer>

e todas ficaram com o tempo de add menor que o tempo do take, seja ele o tempo total ou o tempo isolado para cada mensagem.

Concordo que a métrica de tempo não é adequada, porém, ao executar o mesmo teste em uma fila bloqueante no C# o cenário é diferente, tenho um take mais rápido que o add.

alguém poderia me explicar o que acontece e se isso é real, alguém já passou por este problema? e como resolveu?

desde já, grato pela ajuda.

6 Respostas

doravan

Quando passei por um problema equivalente, eu utilizei Deque.

E

PriorityBlockingQueue é ilimitada (e isso é realmente um problema); você não deveria estar fazendo esse tipo de questionamento (“se o add é mais rápido que o take, então a fila não presta, porque implementei o teste de forma que ele só funcione se o add seja mais lento que o take, e portanto o tamanho da minha fila sempre será por volta de 0 ou 1”).

E se o autor (Doug Lea) pensou em deixar ambas operações as mais rápidas possíveis, e como subproduto isso acabou fazendo que o add seja mais rápido, na sua máquina, que o take?

Isso nunca deveria ser um problema para você. Pode ser que, por um defeito de implementação no .NET Framework da versão que você está usando, o add seja mais lento que o take :frowning:

Pelo que imagino, se você precisa de uma limitação no número de elementos na fila (depois disso, o produtor deve bloquear para evitar exceder a capacidade de consumo da fila), você tem de usar alguma coisa como um ArrayBlockingQueue com um limite fixo.

ops.rio

Olá doravan,

obrigado pela dica, vou testá-la amanhã e digo o resultado aqui.

já você entanglement leia novamente a minha dúvida, acho que você não entendeu o questionamento. :wink:

ops.rio

olá doravan,

cara, fiz uns testes básicos aqui e realmente utilizando uma Deque o take fica mais rápido que o add, ao longo do dia efetuarei testes com a aplicação de fato, porém, acredito que o problema será resolvido :slight_smile:

tks

gomesrod

Eu tinha pensado no seguinte, mas não estava muito seguro da resposta até que o entanglement esclareceu (eu tinha um pressentimento que ele apareceria neste tópico…):

A API não garante nem mesmo sugere que o tempo do take seja menor que o do add. Eles simplesmente implementam ambas as operações da melhor forma possível (ou você preferiria que colocassem um sleep no add só para demorar mais? :slight_smile: ).

Isso não deveria ser um problema, pois dificilmente o gargalo estará no take propriamente dito. Afinal de contas a aplicação fará alguma coisa com a mensagem, que geralmente envolve banco de dados ou IO, operações que fazem com que o tempo do take se torne insignificante.
Nesse caso seria necessário ir à raiz do problema, tentando melhorar a performance do processo receptor.

ops.rio

olá gomesrod,

justamente por este detalhe resolvi isolar o teste de qq outro processamento com a mensagem retirada da fila, como pode perceber no teste acima, após o take eu só calculo o tempo e mais nada, justamente para não ter acréscimos.

mas o primeiro teste com Deque foi satisfatório, vamos ver os outros ao longo do dia, amanhã, ou hj no fim do dia eu coloco os resultados aqui para vcs.

vlw pela força galera.
:smiley:

Criado 25 de outubro de 2011
Ultima resposta 26 de out. de 2011
Respostas 6
Participantes 4