From 8be1568a5d885b713e0172ffe9b50a200dd4181d Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Tue, 21 Dec 2021 15:37:21 +0100 Subject: [PATCH] Increase performance of EnhancedDebugger When using the Enhanced Debugger, a UI application is very noticably slow, especially around establishing a new session. Profiling shows that much of the overhead is caused by XMPP-data being added to the text areas that are on the "raw sent packets" and "raw received packets" tabs of the debugger. This commit improves performance (considerably) by: - properly limiting the amount of lines in those text areas (this was intended but broken in the old implementation) - buffering data to be added in batches, to reduce the amount of invocations to JTextChat.append() As an aside: some newline-based formatting was removed. As the provided data is now already formatted, retaining that did not make much sense anymore. --- .../smackx/debugger/EnhancedDebugger.java | 138 +++++++++++------- 1 file changed, 89 insertions(+), 49 deletions(-) diff --git a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java index c4a8c90dd..3dd474f47 100644 --- a/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java +++ b/smack-debug/src/main/java/org/jivesoftware/smackx/debugger/EnhancedDebugger.java @@ -34,7 +34,13 @@ import java.io.Reader; import java.io.Writer; import java.net.URL; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; import java.util.Date; +import java.util.List; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -419,37 +425,52 @@ public class EnhancedDebugger extends SmackDebugger { // Create a special Reader that wraps the main Reader and logs data to the GUI. ObservableReader debugReader = new ObservableReader(reader); readerListener = new ReaderListener() { - @Override - public void read(final String str) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && - !EnhancedDebuggerWindow.getInstance().isVisible()) { - // Do not add content if the parent is not visible - return; - } + private final PriorityBlockingQueue buffer = new PriorityBlockingQueue<>(); - int index = str.lastIndexOf(">"); - if (index != -1) { - if (receivedText.getLineCount() >= EnhancedDebuggerWindow.MAX_TABLE_ROWS) { - try { - receivedText.replaceRange("", 0, receivedText.getLineEndOffset(0)); - } - catch (BadLocationException e) { - LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); - } - } - receivedText.append(str.substring(0, index + 1)); - receivedText.append(NEWLINE); - if (str.length() > index) { - receivedText.append(str.substring(index + 1)); - } + @Override + public void read(final String line) { + + buffer.add(line); + + SwingUtilities.invokeLater(() -> { + List linesToAdd = new ArrayList<>(); + String data; + Instant start = Instant.now(); + try { + // To reduce overhead/increase performance, try to process up to a certain amount of lines at the + // same time, when they arrive in rapid succession. + while (linesToAdd.size() < 50 + && Duration.between(start, Instant.now()).compareTo(Duration.ofMillis(100)) < 0 + && (data = buffer.poll(10, TimeUnit.MILLISECONDS)) != null) { + linesToAdd.add(data); } - else { - receivedText.append(str); + } catch (InterruptedException e) { + // Interrupted wait-for-poll. Process all data now. + } + + if (linesToAdd.isEmpty()) { + return; + } + + if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && + !EnhancedDebuggerWindow.getInstance().isVisible()) { + // Do not add content if the parent is not visible + return; + } + + // Delete lines from the top, if lines to be added will exceed the maximum. + int linesToDelete = receivedText.getLineCount() + linesToAdd.size() - EnhancedDebuggerWindow.MAX_TABLE_ROWS; + if (linesToDelete > 0) { + try { + receivedText.replaceRange("", 0, receivedText.getLineEndOffset(linesToDelete - 1)); + } + catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); } } + + // Add the new content. + receivedText.append(String.join(NEWLINE, linesToAdd)); }); } }; @@ -458,34 +479,53 @@ public class EnhancedDebugger extends SmackDebugger { // Create a special Writer that wraps the main Writer and logs data to the GUI. ObservableWriter debugWriter = new ObservableWriter(writer); writerListener = new WriterListener() { + private final PriorityBlockingQueue buffer = new PriorityBlockingQueue<>(); + @Override - public void write(final String str) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && - !EnhancedDebuggerWindow.getInstance().isVisible()) { - // Do not add content if the parent is not visible - return; - } + public void write(final String line) { - if (sentText.getLineCount() >= EnhancedDebuggerWindow.MAX_TABLE_ROWS) { - try { - sentText.replaceRange("", 0, sentText.getLineEndOffset(0)); - } - catch (BadLocationException e) { - LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); - } - } + buffer.add(line); - sentText.append(str); - if (str.endsWith(">")) { - sentText.append(NEWLINE); + SwingUtilities.invokeLater(() -> { + List linesToAdd = new ArrayList<>(); + String data; + Instant start = Instant.now(); + try { + // To reduce overhead/increase performance, try to process up to a certain amount of lines at the + // same time, when they arrive in rapid succession. + while (linesToAdd.size() < 50 + && Duration.between(start, Instant.now()).compareTo(Duration.ofMillis(100)) < 0 + && (data = buffer.poll(10, TimeUnit.MILLISECONDS)) != null) { + linesToAdd.add(data); + } + } catch (InterruptedException e) { + // Interrupted wait-for-poll. Process all data now. + } + + if (linesToAdd.isEmpty()) { + return; + } + + if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && + !EnhancedDebuggerWindow.getInstance().isVisible()) { + // Do not add content if the parent is not visible + return; + } + + // Delete lines from the top, if lines to be added will exceed the maximum. + int linesToDelete = sentText.getLineCount() + linesToAdd.size() - EnhancedDebuggerWindow.MAX_TABLE_ROWS; + if (linesToDelete > 0) { + try { + sentText.replaceRange("", 0, sentText.getLineEndOffset(linesToDelete - 1)); + } + catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); } } + + // Add the new content. + sentText.append(String.join(NEWLINE, linesToAdd)); }); - - } }; debugWriter.addWriterListener(writerListener);