Core: Adapt output window chunk size and queue timer dynamically

If we notice that our formatter takes longer to handle an incoming chunk
of output than the amount of time we wait until we handle the next one,
then we lower the chunk size and increase the interval. This way, we
ensure responsiveness in the presence of excessive output.

Task-number: QTCREATORBUG-30135
Change-Id: I1d1e31a7c6f26b50bdc048322e0f7987ddd7b317
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2024-08-20 17:38:55 +02:00
parent adc5153b5d
commit 5553ce0833

View File

@@ -37,11 +37,15 @@
#include <numeric> #include <numeric>
const int chunkSize = 10000;
using namespace Utils; using namespace Utils;
using namespace std::chrono_literals; using namespace std::chrono_literals;
const int defaultChunkSize = 10000;
const int minChunkSize = 1000;
const auto defaultInterval = 10ms;
const auto maxInterval = 1000ms;
namespace Core { namespace Core {
namespace Internal { namespace Internal {
@@ -70,6 +74,7 @@ public:
QTextCursor cursor; QTextCursor cursor;
QString filterText; QString filterText;
int lastFilteredBlockNumber = -1; int lastFilteredBlockNumber = -1;
int chunkSize = defaultChunkSize;
QPalette originalPalette; QPalette originalPalette;
OutputWindow::FilterModeFlags filterMode = OutputWindow::FilterModeFlag::Default; OutputWindow::FilterModeFlags filterMode = OutputWindow::FilterModeFlag::Default;
int beforeContext = 0; int beforeContext = 0;
@@ -97,7 +102,7 @@ OutputWindow::OutputWindow(Context context, const Key &settingsKey, QWidget *par
d->formatter.setPlainTextEdit(this); d->formatter.setPlainTextEdit(this);
d->queueTimer.setSingleShot(true); d->queueTimer.setSingleShot(true);
d->queueTimer.setInterval(10ms); d->queueTimer.setInterval(defaultInterval);
connect(&d->queueTimer, &QTimer::timeout, this, &OutputWindow::handleNextOutputChunk); connect(&d->queueTimer, &QTimer::timeout, this, &OutputWindow::handleNextOutputChunk);
d->settingsKey = settingsKey; d->settingsKey = settingsKey;
@@ -492,7 +497,7 @@ void OutputWindow::handleNextOutputChunk()
// We want to break off the chunks along line breaks, if possible. // We want to break off the chunks along line breaks, if possible.
// Otherwise we can get ugly temporary artifacts e.g. for ANSI escape codes. // Otherwise we can get ugly temporary artifacts e.g. for ANSI escape codes.
int actualChunkSize = std::min(chunkSize, int(chunk.first.size())); int actualChunkSize = std::min(d->chunkSize, int(chunk.first.size()));
const int minEndPos = std::max(0, actualChunkSize - 1000); const int minEndPos = std::max(0, actualChunkSize - 1000);
for (int i = actualChunkSize - 1; i >= minEndPos; --i) { for (int i = actualChunkSize - 1; i >= minEndPos; --i) {
if (chunk.first.at(i) == '\n') { if (chunk.first.at(i) == '\n') {
@@ -544,7 +549,13 @@ void OutputWindow::handleOutputChunk(const QString &output, OutputFormat format)
} }
} }
QElapsedTimer formatterTimer;
formatterTimer.start();
d->formatter.appendMessage(out, format); d->formatter.appendMessage(out, format);
if (formatterTimer.elapsed() > d->queueTimer.interval()) {
d->queueTimer.setInterval(std::min(maxInterval, d->queueTimer.intervalAsDuration() * 2));
d->chunkSize = std::max(minChunkSize, d->chunkSize / 2);
}
if (d->scrollToBottom) { if (d->scrollToBottom) {
if (d->lastMessage.elapsed() < 5) { if (d->lastMessage.elapsed() < 5) {
@@ -659,7 +670,7 @@ void OutputWindow::flush()
{ {
const int totalQueuedSize = std::accumulate(d->queuedOutput.cbegin(), d->queuedOutput.cend(), 0, const int totalQueuedSize = std::accumulate(d->queuedOutput.cbegin(), d->queuedOutput.cend(), 0,
[](int val, const QPair<QString, OutputFormat> &c) { return val + c.first.size(); }); [](int val, const QPair<QString, OutputFormat> &c) { return val + c.first.size(); });
if (totalQueuedSize > 5 * chunkSize) { if (totalQueuedSize > 5 * d->chunkSize) {
d->flushRequested = true; d->flushRequested = true;
return; return;
} }