forked from qt-creator/qt-creator
Measure the time spent on starting and waiting for start
Measure it on demand. The printout will appear when QTC_MEASURE_PROCESS env variable is set. It may be easily combined with QTC_USE_QPROCESS to compare the time spent on freezing Creator when starting Creator or when loading a big project, like Creator project. In order to compare the measurement between old implementation (using directly QProcess) and new implementation (using process launcher) of QtcProcess do the following (linux): 1. To measure the new implementation: QTC_MEASURE_PROCESS= ./bin/qtcreator 2. To measure the old implementation: QTC_USE_QPROCESS= QTC_MEASURE_PROCESS= ./bin/qtcreator How to measure (two scenarios, A and B): A. Launch Creator (don't load any project), wait a bit until all processes finished. Look for the last value in the 11th column ([A/M] Total Measurement, A means all measured functions, M means in main thread only). This value has the greatest influence on GUI freeze - the higher it is, the longer the GUI is frozen. Measure it with 1. (new) and 2. (old) implementation and share your results. B. Launch Creator and load a Creator project. Wait until all processes finished (taskbar should be empty). Look for the last value in the 11th column. Measure it with 1. (new) and 2. (old) implementation and share your results. C* (Additional one) If only you notice that launching a certain QtcProcess takes suspiciously too long, replay your scenario that triggers this process with 1. and 2. and share your results of the 11th column. Change-Id: I549c0a9fd0b3d6223f3d27288130553d99ab768e Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QLoggingCategory>
|
||||
#include <QScopeGuard>
|
||||
#include <QTextCodec>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
@@ -49,7 +50,9 @@
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -64,6 +67,120 @@ using namespace Utils::Internal;
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
|
||||
class MeasureAndRun
|
||||
{
|
||||
public:
|
||||
MeasureAndRun(const char *functionName)
|
||||
: m_functionName(functionName)
|
||||
, m_measureProcess(qEnvironmentVariableIsSet("QTC_MEASURE_PROCESS"))
|
||||
{}
|
||||
template <typename Function, typename... Args>
|
||||
std::invoke_result_t<Function, Args...> measureAndRun(Function &&function, Args&&... args)
|
||||
{
|
||||
if (!m_measureProcess)
|
||||
return std::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
auto cleanup = qScopeGuard([this, &timer] {
|
||||
const qint64 currentNsecs = timer.nsecsElapsed();
|
||||
const bool isMainThread = QThread::currentThread() == qApp->thread();
|
||||
const int hitThisAll = m_hitThisAll.fetch_add(1) + 1;
|
||||
const int hitAllAll = m_hitAllAll.fetch_add(1) + 1;
|
||||
const int hitThisMain = isMainThread
|
||||
? m_hitThisMain.fetch_add(1) + 1
|
||||
: m_hitThisMain.load();
|
||||
const int hitAllMain = isMainThread
|
||||
? m_hitAllMain.fetch_add(1) + 1
|
||||
: m_hitAllMain.load();
|
||||
const qint64 totalThisAll = toMs(m_totalThisAll.fetch_add(currentNsecs) + currentNsecs);
|
||||
const qint64 totalAllAll = toMs(m_totalAllAll.fetch_add(currentNsecs) + currentNsecs);
|
||||
const qint64 totalThisMain = toMs(isMainThread
|
||||
? m_totalThisMain.fetch_add(currentNsecs) + currentNsecs
|
||||
: m_totalThisMain.load());
|
||||
const qint64 totalAllMain = toMs(isMainThread
|
||||
? m_totalAllMain.fetch_add(currentNsecs) + currentNsecs
|
||||
: m_totalAllMain.load());
|
||||
printMeasurement(QLatin1String(m_functionName), hitThisAll, toMs(currentNsecs),
|
||||
totalThisAll, hitAllAll, totalAllAll, isMainThread,
|
||||
hitThisMain, totalThisMain, hitAllMain, totalAllMain);
|
||||
});
|
||||
return std::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
|
||||
}
|
||||
private:
|
||||
static void printHeader()
|
||||
{
|
||||
// [function/thread]: function:(T)his|(A)ll, thread:(M)ain|(A)ll
|
||||
qDebug() << "+----------------+-------+---------+----------+-------+----------+---------+-------+----------+-------+----------+";
|
||||
qDebug() << "| [Function/Thread] = [(T|A)/(M|A)], where: (T)his function, (A)ll functions / threads, (M)ain thread |";
|
||||
qDebug() << "+----------------+-------+---------+----------+-------+----------+---------+-------+----------+-------+----------+";
|
||||
qDebug() << "| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |";
|
||||
qDebug() << "| | [T/A] | [T/A] | [T/A] | [A/A] | [A/A] | | [T/M] | [T/M] | [A/M] | [A/M] |";
|
||||
qDebug() << "| Function | Hit | Current | Total | Hit | Total | Current | Hit | Total | Hit | Total |";
|
||||
qDebug() << "| Name | Count | Measu- | Measu- | Count | Measu- | is Main | Count | Measu- | Count | Measu- |";
|
||||
qDebug() << "| | | rement | rement | | rement | Thread | | rement | | rement |";
|
||||
qDebug() << "+----------------+-------+---------+----------+-------+----------+---------+-------+----------+-------+----------+";
|
||||
}
|
||||
static void printMeasurement(const QString &functionName, int hitThisAll, int currentNsecs,
|
||||
int totalThisAll, int hitAllAll, int totalAllAll, bool isMainThread,
|
||||
int hitThisMain, int totalThisMain, int hitAllMain, int totalAllMain)
|
||||
{
|
||||
static const int repeatHeaderLineCount = 25;
|
||||
if (s_lineCounter.fetch_add(1) % repeatHeaderLineCount == 0)
|
||||
printHeader();
|
||||
|
||||
const QString &functionNameField = QString("%1").arg(functionName, 14);
|
||||
const QString &hitThisAllField = formatField(hitThisAll, 5);
|
||||
const QString ¤tNsecsField = formatField(currentNsecs, 7, " ms");
|
||||
const QString &totalThisAllField = formatField(totalThisAll, 8, " ms");
|
||||
const QString &hitAllAllField = formatField(hitAllAll, 5);
|
||||
const QString &totalAllAllField = formatField(totalAllAll, 8, " ms");
|
||||
const QString &mainThreadField = isMainThread ? QString("%1").arg("yes", 7)
|
||||
: QString("%1").arg("no", 7);
|
||||
const QString &hitThisMainField = formatField(hitThisMain, 5);
|
||||
const QString &totalThisMainField = formatField(totalThisMain, 8, " ms");
|
||||
const QString &hitAllMainField = formatField(hitAllMain, 5);
|
||||
const QString &totalAllMainField = formatField(totalAllMain, 8, " ms");
|
||||
|
||||
const QString &totalString = QString("| %1 | %2 | %3 | %4 | %5 | %6 | %7 | %8 | %9 | %10 | %11 |")
|
||||
.arg(functionNameField, hitThisAllField, currentNsecsField,
|
||||
totalThisAllField, hitAllAllField, totalAllAllField, mainThreadField,
|
||||
hitThisMainField, totalThisMainField, hitAllMainField, totalAllMainField);
|
||||
qDebug("%s", qPrintable(totalString));
|
||||
}
|
||||
static QString formatField(int number, int fieldWidth, const QString &suffix = QString())
|
||||
{
|
||||
return QString("%1%2").arg(number, fieldWidth - suffix.count()).arg(suffix);
|
||||
}
|
||||
|
||||
static int toMs(quint64 nsesc) // nanoseconds to miliseconds
|
||||
{
|
||||
static const int halfMillion = 500000;
|
||||
static const int million = 2 * halfMillion;
|
||||
return int((nsesc + halfMillion) / million);
|
||||
}
|
||||
|
||||
const bool m_measureProcess;
|
||||
const char *m_functionName;
|
||||
std::atomic_int m_hitThisAll = 0;
|
||||
std::atomic_int m_hitThisMain = 0;
|
||||
std::atomic_int64_t m_totalThisAll = 0;
|
||||
std::atomic_int64_t m_totalThisMain = 0;
|
||||
static std::atomic_int m_hitAllAll;
|
||||
static std::atomic_int m_hitAllMain;
|
||||
static std::atomic_int64_t m_totalAllAll;
|
||||
static std::atomic_int64_t m_totalAllMain;
|
||||
static std::atomic_int s_lineCounter;
|
||||
};
|
||||
|
||||
std::atomic_int MeasureAndRun::m_hitAllAll = 0;
|
||||
std::atomic_int MeasureAndRun::m_hitAllMain = 0;
|
||||
std::atomic_int64_t MeasureAndRun::m_totalAllAll = 0;
|
||||
std::atomic_int64_t MeasureAndRun::m_totalAllMain = 0;
|
||||
std::atomic_int MeasureAndRun::s_lineCounter = 0;
|
||||
|
||||
static MeasureAndRun s_start = MeasureAndRun("start");
|
||||
static MeasureAndRun s_waitForStarted = MeasureAndRun("waitForStarted");
|
||||
|
||||
enum { debug = 0 };
|
||||
enum { syncDebug = 0 };
|
||||
|
||||
@@ -389,7 +506,7 @@ public:
|
||||
{
|
||||
const FilePath programFilePath = resolve(m_workingDirectory, FilePath::fromString(program));
|
||||
if (programFilePath.exists() && programFilePath.isExecutableFile()) {
|
||||
m_process->start(programFilePath.toString(), arguments, writeData);
|
||||
s_start.measureAndRun(&ProcessInterface::start, m_process, program, arguments, writeData);
|
||||
} else {
|
||||
m_process->setErrorString(QLatin1String(
|
||||
"The program \"%1\" does not exist or is not executable.").arg(program));
|
||||
@@ -927,7 +1044,7 @@ qint64 QtcProcess::processId() const
|
||||
|
||||
bool QtcProcess::waitForStarted(int msecs)
|
||||
{
|
||||
return d->m_process->waitForStarted(msecs);
|
||||
return s_waitForStarted.measureAndRun(&ProcessInterface::waitForStarted, d->m_process, msecs);
|
||||
}
|
||||
|
||||
bool QtcProcess::waitForReadyRead(int msecs)
|
||||
|
Reference in New Issue
Block a user