Tenho um JApplet que contém um JComponent (que serve como Canvas para gráficos) e dois objetos JTable; um contém 4 linhas e 2 colunas e o outro contém 1 linha e 2 colunas.
Cada célula dessas tabelas contém um dado numérico em formato double.
100 vezes por segundo, o programa faz cálculos, atualiza a imagem do JComponent e exibe nos dois JTable os dados numéricos calculados, utilizando o setValueAt dos TableModel das duas tabelas.
Num computador com Windows 7, processador Pentium Dual CPU E2160 1,80 GHz e 2 GB de memória, o processo (imagem no JComponent + exibição dos dados nas tabelas) fica na velocidade esperada; no entanto, num computador com processador Pentium 4 2,66 GHz e 1,25 GB de memória, o processo fica lento.
O cálculo e a geração da imagem no JComponent estão rápidos. O problema são as tabelas.
Nesse último computador, rodando o processo com o desenho da imagem no JComponent, mas sem redesenhar as tabelas, o processo fica rápido.
Então, deve haver alguma coisa errada no processo de desenho da tabela.
Eu tentei implementar os métodos descritos aqui: http://java.sun.com/products/jfc/tsc/articles/ChristmasTree/
mas não fizeram nenhuma diferença.
Eu também tentei fazer o seguinte: em vez de chamar a função fireTableCellUpdated() dentro do setValueAt do Table Model, chamar a função fireTableDataChanged() para redesenhar as tabelas inteiras somente após ter inserido todos os valores nas tabelas. Isso provocou uma pequena melhora na velocidade, mas ainda fica lento.
Alguém tem alguma ideia?
Me avisem se faltou alguma informação importante aqui.
Obrigado.
TableModel utilizado nos dois JTable:
public class jtableModel extends AbstractTableModel {
Object[][] rowData;
String[] columnNames;
public jtableModel(Object[][] rd, String[] cn) {
rowData = rd;
columnNames = cn;
}
@Override public Class<?> getColumnClass(int coluna) {
return Object.class; //Eu não coloquei Double.class aqui porque, em alguns casos, o valor que aparece em uma célula é um string.
}
@Override public String getColumnName(int col) {
return columnNames[col].toString();
}
public int getRowCount() { return rowData.length; }
public int getColumnCount() { return columnNames.length; }
public Object getValueAt(int row, int col) {
return rowData[row][col];
}
@Override public boolean isCellEditable(int row, int col)
{
return false;
}
@Override public void setValueAt(Object value, int row, int col) {
rowData[row][col] = value;
fireTableCellUpdated(row, col); //Se essa linha for colocada como comentário, as células da tabela não são redesenhadas e o processo fica rápido.
}
}
QUem vai conseguir analizar esses dados nessa velocidade? Eu com certeza daria um delay para pelo menos o que o olho humano consegue ver… De que serve uma tela mudando nessa velocidade?
Apesar de a taxa de atualização dos dados ser alta, a sua taxa de variação é pequena, de forma que é possível observar a sua mudança a uma velocidade boa.
[quote=Marky.Vasconcelos]Como voce verificou a velocidade do processo? Usou um profiler?
PS: Seu TableModel devia ter uma lista de objetos invés de ser uma copia do DefaultTableModel (trabalhando com Object[][]).
Até no TableModel que fiz (ObjectTableModel) que é totalmente baseado em Reflection trazer os dados para a tabela sempre foi extremamente rapido.[/quote]
Eu sou iniciante em Java.
Na verdade, eu simplesmente verifiquei que o processo foi mais lento, mas sem fazer nenhuma medição.
A lentidão pode estar relacionada ao TableModel? Eu achava que não, porque, mesmo trazendo os dados para a tabela, mas impedindo as células de se redesenharem (removendo o fireTableCellUpdated() do setValueAt), o processo fica rápido.
Dica: A taxa de atualização do seu monitor dificilmente será maior que 65 quadros por segundo. Ou seja, 100 atualizações por segundo não vai ocorrer, a menos que você troque de hardware. Ainda que ocorra, seu usuário só conseguirá enxergar alguma coisa se for uma libélula.
Se você não sabe porque está lento, rode sua aplicação num profiler, como o Visual VM, que vem junto com o JDK. Ou com o profiler do Netbeans (é a opção perfil). Ele exibirá exatamente o motivo da lentidão.
Provavelmente é o processo de desenho da tabela. Só não vale a pena chamar update tantas vezes. Tente enfileirar o processamento e só dar update ocasionalmente. E o ideal é evitar o fireTableDataChanged, e sim, usar o método que requisite o menor número de updates possível.
Eu nunca tinha usado um profiler antes. Eu usei o profiler do NetBeans, e, se eu não entendi errado, parece que realmente o problema é o redesenho da tabela. Nos tempod para a função que insere os valores da tabela e faz o fireTableDataChanged, o “tempo em si” foi de aproximadamente 65% do tempo total da função run do applet, e o seu tempo total foi de aproximadamente 73%, em uma das medições.
Pode perguntar se precisar de mais algum detalhe; eu não tenho muita certeza de que detalhes eu deveria incluir aqui.
Então, o que eu deveria fazer é melhorar o processo que controla o número de quadros por segundo, de modo a não obter tantos? Para a precisão nos cálculos, eu quereria manter ao menos a taxa dos cálculos (não necessariamente o número de quadros por segundo) como 100 vezes por segundo.
O que você quer dizer exatamente por “o método que requisite o menor número de updates possível”? Qual outro método existe além do fireTableDataChanged para redesenhar a tabela?
Use métodos como:
fireTableCellUpdated, se você souber a célula exata que mudou
fireTableRowsUpdated, se você atualizou um conjunto de linhas. Preferencialmente, passando como parâmetro as linhas atualizadas.
Esses métodos evitam o refresh da tela inteira. Muitas vezes o java só repintará a linha ou a célula atualizada.
Mas uma boa é mesmo fazer atualizações na taxa de 100 updates por segundo, mas agendar uma thread para chamar o fireTableDataChanged em menos tempo, a cada meio segundo, ou um segundo.
Como vc mesmo constatou no profiler, é impossível repintar tão rápido.
[quote=ViniGodoy]Use métodos como:
fireTableCellUpdated, se você souber a célula exata que mudou
fireTableRowsUpdated, se você atualizou um conjunto de linhas. Preferencialmente, passando como parâmetro as linhas atualizadas.
Esses métodos evitam o refresh da tela inteira. Muitas vezes o java só repintará a linha ou a célula atualizada.[/quote]
Pelo que eu pude ver, do jeito como está agora, chamar o fireTableDataChanged depois de ter chamado todos os setValueAt em vez de chamar o o método fireTableCellUpdated a cada setValueAt faz o processo ficar mais rápido.
[quote=ViniGodoy]Mas uma boa é mesmo fazer atualizações na taxa de 100 updates por segundo, mas agendar uma thread para chamar o fireTableDataChanged em menos tempo, a cada meio segundo, ou um segundo.
Como vc mesmo constatou no profiler, é impossível repintar tão rápido.[/quote]
Então, parece que, com a tabela atualizando 100 vezes por segundo, é inevitável gerar lentidão.
Eu poderia colocar no método run do Thread uma variável int numero = 0 que é incrementada a cada quadro, e quando chega a 25 provoca a chamada da função que exibe os valores na tabela, e então assume o valor 0? Eu fiz isso, e agora as tabelas são atualizadas apenas 4 vezes por segundo. Isso certamente diminui o peso do redesenho das tabelas. Agora, o repaint do canvas ocupa 70% do tempo, e o tempo total que levam as chamadas dos métodos do run é muito inferior ao tempo total durante o qual o run ficou rodando. Não sei se é a melhor forma.
E, com certeza, chamar o tableDataChanged se vc atualizou diversas células é melhor do que chamar o update em célula por célula. Escolher entre um ou outro depende muito do perfil da sua aplicação e, se a sua atualiza mesmo a tabela como um todo, é melhor chamar o DataChanged em intervalos de tempo regulares, como vc fez.