cache results of feature search
authorOswald Buddenhagen <oswald.buddenhagen@digia.com>
Mon, 3 Jun 2013 17:08:10 +0000 (19:08 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 26 Aug 2013 21:54:43 +0000 (23:54 +0200)
looking up the same files in the same locations over and over again
is a rather significant waste. in particular, looking up the CONFIG
flags that don't correspond with features has a measurable impact on qt
creator's project loading time.

Task-number: QTCREATORBUG-9154
Change-Id: Ibae3d8b7797e706a6416a7d45c77734ab1281b51
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
(cherry picked from qtcreator/fa27cd79e05aed4ebd16d5648480cc7d48fefd43)

src/linguist/shared/qmakeevaluator.cpp
src/linguist/shared/qmakeevaluator.h

index e080c15..281385a 100644 (file)
@@ -1499,7 +1499,7 @@ void QMakeEvaluator::updateFeaturePaths()
     foreach (const QString &root, feature_roots)
         if (IoUtils::exists(root))
             ret << root;
-    m_featureRoots = ret;
+    m_featureRoots = new QMakeFeatureRoots(ret);
 }
 
 ProString QMakeEvaluator::propertyValue(const ProKey &name) const
@@ -1857,35 +1857,55 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile(
     if (!fn.endsWith(QLatin1String(".prf")))
         fn += QLatin1String(".prf");
 
-    if (m_featureRoots.isEmpty())
+    if (!m_featureRoots)
         updateFeaturePaths();
-    int start_root = 0;
+#ifdef PROEVALUATOR_THREAD_SAFE
+    m_featureRoots->mutex.lock();
+#endif
     QString currFn = currentFileName();
-    if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
-        QStringRef currPath = IoUtils::pathName(currFn);
-        for (int root = 0; root < m_featureRoots.size(); ++root)
-            if (currPath == m_featureRoots.at(root)) {
-                start_root = root + 1;
-                break;
+    if (IoUtils::fileName(currFn) != IoUtils::fileName(fn))
+        currFn.clear();
+    // Null values cannot regularly exist in the hash, so they indicate that the value still
+    // needs to be determined. Failed lookups are represented via non-null empty strings.
+    QString *fnp = &m_featureRoots->cache[qMakePair(fn, currFn)];
+    if (fnp->isNull()) {
+        int start_root = 0;
+        const QStringList &paths = m_featureRoots->paths;
+        if (!currFn.isEmpty()) {
+            QStringRef currPath = IoUtils::pathName(currFn);
+            for (int root = 0; root < paths.size(); ++root)
+                if (currPath == paths.at(root)) {
+                    start_root = root + 1;
+                    break;
+                }
+        }
+        for (int root = start_root; root < paths.size(); ++root) {
+            QString fname = paths.at(root) + fn;
+            if (IoUtils::exists(fname)) {
+                fn = fname;
+                goto cool;
             }
-    }
-    for (int root = start_root; root < m_featureRoots.size(); ++root) {
-        QString fname = m_featureRoots.at(root) + fn;
-        if (IoUtils::exists(fname)) {
-            fn = fname;
-            goto cool;
         }
-    }
 #ifdef QMAKE_BUILTIN_PRFS
-    fn.prepend(QLatin1String(":/qmake/features/"));
-    if (QFileInfo(fn).exists())
-        goto cool;
+        fn.prepend(QLatin1String(":/qmake/features/"));
+        if (QFileInfo(fn).exists())
+            goto cool;
 #endif
-    if (!silent)
-        evalError(fL1S("Cannot find feature %1").arg(fileName));
-    return ReturnFalse;
+        fn = QLatin1String(""); // Indicate failed lookup. See comment above.
 
-  cool:
+      cool:
+        *fnp = fn;
+    } else {
+        fn = *fnp;
+    }
+#ifdef PROEVALUATOR_THREAD_SAFE
+    m_featureRoots->mutex.unlock();
+#endif
+    if (fn.isEmpty()) {
+        if (!silent)
+            evalError(fL1S("Cannot find feature %1").arg(fileName));
+        return ReturnFalse;
+    }
     ProStringList &already = valuesRef(ProKey("QMAKE_INTERNAL_INCLUDED_FEATURES"));
     ProString afn(fn);
     if (already.contains(afn)) {
index f4e65e3..548c2d6 100644 (file)
 #include <qstack.h>
 #include <qstring.h>
 #include <qstringlist.h>
+#include <qshareddata.h>
 #ifndef QT_BOOTSTRAPPED
 # include <qprocess.h>
 #endif
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <qmutex.h>
+#endif
 
 QT_BEGIN_NAMESPACE
 
@@ -83,6 +87,20 @@ public:
     virtual void doneWithEval(ProFile *parent) = 0;
 };
 
+typedef QPair<QString, QString> QMakeFeatureKey; // key, parent
+typedef QHash<QMakeFeatureKey, QString> QMakeFeatureHash;
+
+class QMAKE_EXPORT QMakeFeatureRoots : public QSharedData
+{
+public:
+    QMakeFeatureRoots(const QStringList &_paths) : paths(_paths) {}
+    const QStringList paths;
+    mutable QMakeFeatureHash cache;
+#ifdef PROEVALUATOR_THREAD_SAFE
+    mutable QMutex mutex;
+#endif
+};
+
 // We use a QLinkedList based stack instead of a QVector based one (QStack), so that
 // the addresses of value maps stay constant. The qmake generators rely on that.
 class QMAKE_EXPORT ProValueMapStack : public QLinkedList<ProValueMap>
@@ -283,7 +301,7 @@ public:
     QStringList m_qmakepath;
     QStringList m_qmakefeatures;
     QStringList m_mkspecPaths;
-    QStringList m_featureRoots;
+    QExplicitlySharedDataPointer<QMakeFeatureRoots> m_featureRoots;
     ProString m_dirSep;
     ProFunctionDefs m_functionDefs;
     ProStringList m_returnValue;