From 0fbd768b021b87e6f4e587bb8ec2ae52eaf5f10c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 22 Aug 2018 14:26:45 +0200 Subject: [PATCH 1/3] Improve performance on serial monitor 'Show timestamp' This patch dramatically reduce pressure on heap memory: - use a StringTokenizer instead of the very slow String.split(...) method to decode newlines. This avoid allocation/deallocation of big strings array and use of regexp. - pre allocate as many object as possible to avoid new allocation while streaming data. - the "timestamp" string is calculated only once per iteration. - use StringBuilder instead of inline temporary strings (that are, again, allocated and deallocated on each iteration) --- .../processing/app/AbstractTextMonitor.java | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/app/src/processing/app/AbstractTextMonitor.java b/app/src/processing/app/AbstractTextMonitor.java index 72b5c4dee9f..3149e07cef7 100644 --- a/app/src/processing/app/AbstractTextMonitor.java +++ b/app/src/processing/app/AbstractTextMonitor.java @@ -12,6 +12,7 @@ import java.awt.event.WindowEvent; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.StringTokenizer; import javax.swing.Box; import javax.swing.BoxLayout; @@ -177,21 +178,43 @@ public void onSerialRateChange(ActionListener listener) { public void message(final String s) { SwingUtilities.invokeLater(new Runnable() { + // Pre-allocate all objects used for streaming data + Date t = new Date(); + String now; + StringBuilder out = new StringBuilder(16384); + public void run() { if (addTimeStampBox.isSelected()) { - String[] lines = s.split("(?<=\\n)"); - Document doc = textArea.getDocument(); - for (String currentLine : lines) { - try { - if (doc.getLength() == 0 || ((int) doc.getText(doc.getLength() - 1, 1).charAt(0) == 10)) { - textArea.append(logDateFormat.format(new Date()) + " -> " + currentLine); - } else { - textArea.append(currentLine); - } - } catch (BadLocationException e) { - e.printStackTrace(); + t.setTime(System.currentTimeMillis()); + now = logDateFormat.format(t); + out.setLength(0); + + boolean isStartingLine; + try { + Document doc = textArea.getDocument(); + isStartingLine = doc.getLength() == 0 || ((int) doc.getText(doc.getLength() - 1, 1).charAt(0) == 10); + } catch (BadLocationException e) { + // Should not happen but... + e.printStackTrace(); + return; + } + + StringTokenizer tokenizer = new StringTokenizer(s, "\n", true); + while (tokenizer.hasMoreTokens()) { + if (isStartingLine) { + out.append(now); + out.append(" -> "); + } + out.append(tokenizer.nextToken()); + + // Check if we have a "\n" token + if (tokenizer.hasMoreTokens()) { + out.append(tokenizer.nextToken()); + isStartingLine = true; } } + + textArea.append(out.toString()); } else { textArea.append(s); } From 9f0c5d4b786b8a9f9c95c0b098b0fdabf21466f5 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 22 Aug 2018 14:42:16 +0200 Subject: [PATCH 2/3] Serial monitor timestamp: cache newline status This saves an access to the Document object (as well as a bunch temporary object allocations). --- .../processing/app/AbstractTextMonitor.java | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/app/src/processing/app/AbstractTextMonitor.java b/app/src/processing/app/AbstractTextMonitor.java index 3149e07cef7..60c06701932 100644 --- a/app/src/processing/app/AbstractTextMonitor.java +++ b/app/src/processing/app/AbstractTextMonitor.java @@ -25,9 +25,7 @@ import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; -import javax.swing.text.BadLocationException; import javax.swing.text.DefaultCaret; -import javax.swing.text.Document; import cc.arduino.packages.BoardPort; @@ -182,6 +180,7 @@ public void message(final String s) { Date t = new Date(); String now; StringBuilder out = new StringBuilder(16384); + boolean isStartingLine = false; public void run() { if (addTimeStampBox.isSelected()) { @@ -189,29 +188,16 @@ public void run() { now = logDateFormat.format(t); out.setLength(0); - boolean isStartingLine; - try { - Document doc = textArea.getDocument(); - isStartingLine = doc.getLength() == 0 || ((int) doc.getText(doc.getLength() - 1, 1).charAt(0) == 10); - } catch (BadLocationException e) { - // Should not happen but... - e.printStackTrace(); - return; - } - StringTokenizer tokenizer = new StringTokenizer(s, "\n", true); while (tokenizer.hasMoreTokens()) { if (isStartingLine) { out.append(now); out.append(" -> "); } - out.append(tokenizer.nextToken()); - - // Check if we have a "\n" token - if (tokenizer.hasMoreTokens()) { - out.append(tokenizer.nextToken()); - isStartingLine = true; - } + String token = tokenizer.nextToken(); + out.append(token); + // tokenizer returns "\n" as a single token + isStartingLine = token.charAt(0) == '\n'; } textArea.append(out.toString()); From a5ffb4a5953aaf88a2c5c15712c9d58549f8f1a7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 22 Aug 2018 14:54:02 +0200 Subject: [PATCH 3/3] Serial monitor timestamp: add the arrow ' -> ' to the timestamp format This saves another "append" call while streaming. --- app/src/processing/app/AbstractTextMonitor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/processing/app/AbstractTextMonitor.java b/app/src/processing/app/AbstractTextMonitor.java index 60c06701932..fdfcfba760a 100644 --- a/app/src/processing/app/AbstractTextMonitor.java +++ b/app/src/processing/app/AbstractTextMonitor.java @@ -47,7 +47,7 @@ public abstract class AbstractTextMonitor extends AbstractMonitor { public AbstractTextMonitor(BoardPort boardPort) { super(boardPort); - logDateFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + logDateFormat = new SimpleDateFormat("HH:mm:ss.SSS -> "); } protected void onCreateWindow(Container mainPane) { @@ -192,7 +192,6 @@ public void run() { while (tokenizer.hasMoreTokens()) { if (isStartingLine) { out.append(now); - out.append(" -> "); } String token = tokenizer.nextToken(); out.append(token);