QRegularExpression: add ways to force an immediate optimization

The fact that we kick in a pattern study and possibly a JIT compilation
after an undocumented number of usages is suboptimal, for a number
or reasons: users may want to JIT compile a pattern immediately,
and at the same time they may not want a random delay in the program
(due to the pattern getting optimized at a random usage).

So: add an optimize() call to force an immediate pattern optimization,
and a pattern option to force an optimization on the first usage.

Change-Id: I95efdecfd31f11ca7cceb9c05037df613601a11c
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Giuseppe D'Angelo 2014-04-19 15:11:18 +02:00 committed by The Qt Project
parent eedd300b0e
commit 260f24228b
7 changed files with 183 additions and 7 deletions

View File

@ -699,6 +699,12 @@ QT_BEGIN_NAMESPACE
\c{\w} to match any character with either the Unicode L (letter) or N
(digit) property, plus underscore, and so on. This option corresponds
to the \c{/u} modifier in Perl regular expressions.
\value OptimizeOnFirstUsageOption
The regular expression will be optimized (and possibly
JIT-compiled) on its first usage, instead of after a certain (undefined)
number of usages. See also \l{QRegularExpression::}{optimize()}.
This enum value has been introduced in Qt 5.4.
*/
/*!
@ -802,7 +808,13 @@ struct QRegularExpressionPrivate : QSharedData
void cleanCompiledPattern();
void compilePattern();
void getPatternInfo();
void optimizePattern();
enum OptimizePatternOption {
LazyOptimizeOption,
ImmediateOptimizeOption
};
void optimizePattern(OptimizePatternOption option);
QRegularExpressionMatchPrivate *doMatch(const QString &subject,
int offset,
@ -1107,13 +1119,16 @@ static bool isJitEnabled()
(localStudyData) before using storeRelease on studyData. In doMatch there's
the corresponding loadAcquire.
*/
void QRegularExpressionPrivate::optimizePattern()
void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option)
{
Q_ASSERT(compiledPattern);
QMutexLocker lock(&mutex);
if (studyData.load() || (++usedCount != qt_qregularexpression_optimize_after_use_count))
if (studyData.load()) // already optimized
return;
if ((option == LazyOptimizeOption) && (++usedCount != qt_qregularexpression_optimize_after_use_count))
return;
static const bool enableJit = isJitEnabled();
@ -1233,8 +1248,13 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString
matchType, matchOptions,
capturingCount + 1);
const OptimizePatternOption optimizePatternOption =
(patternOptions & QRegularExpression::OptimizeOnFirstUsageOption)
? ImmediateOptimizeOption
: LazyOptimizeOption;
// this is mutex protected
const_cast<QRegularExpressionPrivate *>(this)->optimizePattern();
const_cast<QRegularExpressionPrivate *>(this)->optimizePattern(optimizePatternOption);
// work with a local copy of the study data, as we are running pcre_exec
// potentially more than once, and we don't want to run call it
@ -1666,6 +1686,27 @@ QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &s
return QRegularExpressionMatchIterator(*priv);
}
/*!
\since 5.4
Forces an immediate optimization of the pattern, including
JIT-compiling it (if the JIT compiler is enabled).
Patterns are normally optimized only after a certain number of usages.
If you can predict that this QRegularExpression object is going to be
used for several matches, it may be convenient to optimize it in
advance by calling this function.
\sa QRegularExpression::OptimizeOnFirstUsageOption
*/
void QRegularExpression::optimize()
{
if (!isValid()) // will compile the pattern
return;
d->optimizePattern(QRegularExpressionPrivate::ImmediateOptimizeOption);
}
/*!
Returns \c true if the regular expression is equal to \a re, or false
otherwise. Two QRegularExpression objects are equal if they have
@ -2327,6 +2368,8 @@ QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOption
flags.append("DontCaptureOption|");
if (patternOptions & QRegularExpression::UseUnicodePropertiesOption)
flags.append("UseUnicodePropertiesOption|");
if (patternOptions & QRegularExpression::OptimizeOnFirstUsageOption)
flags.append("OptimizeOnFirstUsageOption|");
flags.chop(1);
}

View File

@ -69,7 +69,8 @@ public:
ExtendedPatternSyntaxOption = 0x0008,
InvertedGreedinessOption = 0x0010,
DontCaptureOption = 0x0020,
UseUnicodePropertiesOption = 0x0040
UseUnicodePropertiesOption = 0x0040,
OptimizeOnFirstUsageOption = 0x0080
};
Q_DECLARE_FLAGS(PatternOptions, PatternOption)
@ -122,6 +123,8 @@ public:
MatchType matchType = NormalMatch,
MatchOptions matchOptions = NoMatchOption) const;
void optimize();
static QString escape(const QString &str);
bool operator==(const QRegularExpression &re) const;

View File

@ -1,2 +1,3 @@
tst_qregularexpression_alwaysoptimize
tst_qregularexpression_defaultoptimize
tst_qregularexpression_forceoptimize

View File

@ -0,0 +1,8 @@
CONFIG += testcase parallel_test
TARGET = tst_qregularexpression_forceoptimize
QT = core testlib
HEADERS = ../tst_qregularexpression.h
SOURCES = \
tst_qregularexpression_forceoptimize.cpp \
../tst_qregularexpression.cpp
DEFINES += forceOptimize=true

View File

@ -0,0 +1,52 @@
/****************************************************************************
**
** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include "../tst_qregularexpression.h"
class tst_QRegularExpression_ForceOptimize : public tst_QRegularExpression
{
Q_OBJECT
};
QTEST_APPLESS_MAIN(tst_QRegularExpression_ForceOptimize)
#include "tst_qregularexpression_forceoptimize.moc"

View File

@ -1,3 +1,3 @@
TEMPLATE = subdirs
SUBDIRS = defaultoptimize
SUBDIRS = defaultoptimize forceoptimize
contains(QT_CONFIG,private_tests):SUBDIRS += alwaysoptimize

View File

@ -48,6 +48,10 @@
#include "tst_qregularexpression.h"
#ifndef forceOptimize
#define forceOptimize false
#endif
struct Match
{
Match()
@ -329,22 +333,30 @@ void tst_QRegularExpression::gettersSetters()
{
QRegularExpression re;
re.setPattern(pattern);
if (forceOptimize)
re.optimize();
QCOMPARE(re.pattern(), pattern);
QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption);
}
{
QRegularExpression re;
re.setPatternOptions(patternOptions);
if (forceOptimize)
re.optimize();
QCOMPARE(re.pattern(), QString());
QCOMPARE(re.patternOptions(), patternOptions);
}
{
QRegularExpression re(pattern);
if (forceOptimize)
re.optimize();
QCOMPARE(re.pattern(), pattern);
QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption);
}
{
QRegularExpression re(pattern, patternOptions);
if (forceOptimize)
re.optimize();
QCOMPARE(re.pattern(), pattern);
QCOMPARE(re.patternOptions(), patternOptions);
}
@ -385,6 +397,8 @@ void tst_QRegularExpression::escape()
QFETCH(QString, escaped);
QCOMPARE(QRegularExpression::escape(string), escaped);
QRegularExpression re(escaped);
if (forceOptimize)
re.optimize();
QCOMPARE(re.isValid(), true);
}
@ -415,6 +429,8 @@ void tst_QRegularExpression::validity()
QFETCH(QString, pattern);
QFETCH(bool, validity);
QRegularExpression re(pattern);
if (forceOptimize)
re.optimize();
QCOMPARE(re.isValid(), validity);
if (!validity)
QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object");
@ -501,6 +517,9 @@ void tst_QRegularExpression::patternOptions()
QFETCH(QString, subject);
QFETCH(Match, match);
if (forceOptimize)
regexp.optimize();
QRegularExpressionMatch m = regexp.match(subject);
consistencyCheck(m);
QVERIFY(m == match);
@ -717,6 +736,9 @@ void tst_QRegularExpression::normalMatch()
QFETCH(QRegularExpression::MatchOptions, matchOptions);
QFETCH(Match, match);
if (forceOptimize)
regexp.optimize();
{
QRegularExpressionMatch m = regexp.match(subject, offset, QRegularExpression::NormalMatch, matchOptions);
consistencyCheck(m);
@ -995,6 +1017,9 @@ void tst_QRegularExpression::partialMatch()
QFETCH(QRegularExpression::MatchOptions, matchOptions);
QFETCH(Match, match);
if (forceOptimize)
regexp.optimize();
{
QRegularExpressionMatch m = regexp.match(subject, offset, matchType, matchOptions);
consistencyCheck(m);
@ -1286,6 +1311,10 @@ void tst_QRegularExpression::globalMatch()
QFETCH(QRegularExpression::MatchType, matchType);
QFETCH(QRegularExpression::MatchOptions, matchOptions);
QFETCH(QList<Match>, matchList);
if (forceOptimize)
regexp.optimize();
{
QRegularExpressionMatchIterator iterator = regexp.globalMatch(subject, offset, matchType, matchOptions);
consistencyCheck(iterator);
@ -1320,6 +1349,10 @@ void tst_QRegularExpression::serialize()
QFETCH(QString, pattern);
QFETCH(QRegularExpression::PatternOptions, patternOptions);
QRegularExpression outRe(pattern, patternOptions);
if (forceOptimize)
outRe.optimize();
QByteArray buffer;
{
QDataStream out(&buffer, QIODevice::WriteOnly);
@ -1376,16 +1409,34 @@ void tst_QRegularExpression::operatoreq()
{
QRegularExpression re1(pattern);
QRegularExpression re2(pattern);
if (forceOptimize)
re1.optimize();
if (forceOptimize)
re2.optimize();
verifyEquality(re1, re2);
}
{
QRegularExpression re1(QString(), patternOptions);
QRegularExpression re2(QString(), patternOptions);
if (forceOptimize)
re1.optimize();
if (forceOptimize)
re2.optimize();
verifyEquality(re1, re2);
}
{
QRegularExpression re1(pattern, patternOptions);
QRegularExpression re2(pattern, patternOptions);
if (forceOptimize)
re1.optimize();
if (forceOptimize)
re2.optimize();
verifyEquality(re1, re2);
}
}
@ -1414,6 +1465,10 @@ void tst_QRegularExpression::captureCount()
{
QFETCH(QString, pattern);
QRegularExpression re(pattern);
if (forceOptimize)
re.optimize();
QTEST(re.captureCount(), "captureCount");
if (!re.isValid())
QCOMPARE(re.captureCount(), -1);
@ -1480,7 +1535,11 @@ void tst_QRegularExpression::captureNames()
QFETCH(QString, pattern);
QFETCH(StringToIntMap, namedCapturesIndexMap);
const QRegularExpression re(pattern);
QRegularExpression re(pattern);
if (forceOptimize)
re.optimize();
QStringList namedCaptureGroups = re.namedCaptureGroups();
int namedCaptureGroupsCount = namedCaptureGroups.size();
@ -1515,6 +1574,10 @@ void tst_QRegularExpression::pcreJitStackUsage()
QFETCH(QString, subject);
QRegularExpression re(pattern);
if (forceOptimize)
re.optimize();
QVERIFY(re.isValid());
QRegularExpressionMatch match = re.match(subject);
consistencyCheck(match);
@ -1541,6 +1604,10 @@ void tst_QRegularExpression::regularExpressionMatch()
QFETCH(QString, subject);
QRegularExpression re(pattern);
if (forceOptimize)
re.optimize();
QVERIFY(re.isValid());
QRegularExpressionMatch match = re.match(subject);
consistencyCheck(match);
@ -1580,5 +1647,7 @@ void tst_QRegularExpression::JOptionUsage()
QRegularExpression re(pattern);
if (isValid && JOptionUsed)
QTest::ignoreMessage(QtWarningMsg, qPrintable(warningMessage.arg(pattern)));
if (forceOptimize)
re.optimize();
QCOMPARE(re.isValid(), isValid);
}