Use on-disk compilation unit if available

If we succeeded in saving the compilation unit to disk, then attempt to use it
right away. This replaces the C++ heap usage for the compilation unit data as
well as the anonymous allocated executable memory with file-backed mmap'ed
memory. That means the memory can be discarded when overall availability is low
and paged in on-demand.

Change-Id: Ide1b1e11752d861eb049a99a26ca12cec5e2502e
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Simon Hausmann 2016-07-07 15:38:01 +02:00
parent 01575a22ea
commit 234e762265
6 changed files with 25 additions and 12 deletions

View File

@ -424,6 +424,7 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory
return false;
}
const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
QScopedValueRollback<const Unit *> dataPtrChange(data, reinterpret_cast<const Unit *>(cacheData));
{
@ -448,6 +449,7 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory
return false;
dataPtrChange.commit();
free(const_cast<Unit*>(oldDataPtr));
backingFile.reset(cacheFile.take());
return true;
}

View File

@ -666,8 +666,10 @@ struct Unit
return QString();
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
if (flags & StaticData)
return QString::fromRawData(characters, str->size);
// Too risky to do this while we unmap disk backed compilation but keep pointers to string
// data in the identifier tables.
// if (flags & StaticData)
// return QString::fromRawData(characters, str->size);
return QString(characters, str->size);
#else
const LEUInt16 *characters = reinterpret_cast<const LEUInt16 *>(str + 1);

View File

@ -1665,8 +1665,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit
bool CompilationUnit::memoryMapCode(QString *errorString)
{
Q_UNUSED(errorString);
Q_ASSERT(codeRefs.isEmpty());
codeRefs.reserve(data->functionTableSize);
codeRefs.resize(data->functionTableSize);
const char *basePtr = reinterpret_cast<const char *>(data);
@ -1680,7 +1679,7 @@ bool CompilationUnit::memoryMapCode(QString *errorString)
#else
QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize);
#endif
codeRefs.append(code);
codeRefs[i] = code;
}
return true;

View File

@ -129,8 +129,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit
bool CompilationUnit::memoryMapCode(QString *errorString)
{
Q_UNUSED(errorString);
Q_ASSERT(codeRefs.isEmpty());
codeRefs.reserve(data->functionTableSize);
codeRefs.resize(data->functionTableSize);
const char *basePtr = reinterpret_cast<const char *>(data);
@ -139,7 +138,7 @@ bool CompilationUnit::memoryMapCode(QString *errorString)
void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset));
JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr));
JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize);
codeRefs.append(codeRef);
codeRefs[i] = codeRef;
}
return true;

View File

@ -2502,7 +2502,8 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache,
{
Q_ASSERT(m_compiledData.isNull());
QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data(), importCache, resolvedTypeCache);
QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), importCache, resolvedTypeCache);
m_compiledData = compiler.compile();
if (!m_compiledData) {
setError(compiler.compilationErrors());
@ -2510,7 +2511,12 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache,
}
if (diskCache() || forceDiskCacheRefresh()) {
QString errorString;
if (!m_compiledData->saveToDisk(url(), &errorString)) {
if (m_compiledData->saveToDisk(url(), &errorString)) {
QString error;
if (!m_compiledData->loadFromDisk(url(), enginePrivate->v4engine()->iselFactory.data(), &error)) {
// ignore error, keep using the in-memory compilation unit.
}
} else {
qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString;
}
}

View File

@ -2118,8 +2118,13 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode()
QQmlTypeData *td = eng->typeLoader.getType(url);
Q_ASSERT(td);
const QV4::CompiledData::Unit *qmlUnit = td->compilationUnit()->data;
Q_ASSERT(qmlUnit);
const QV4::CompiledData::Unit *readOnlyQmlUnit = td->compilationUnit()->data;
Q_ASSERT(readOnlyQmlUnit);
QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize));
memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize);
qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
td->compilationUnit()->data = qmlUnit;
const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject);
QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject"));
quint32 i;