From 220b0239c3ead9580ef3378356046d5fc908ea1d Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Mon, 1 May 2017 14:12:01 -0700 Subject: [PATCH] Bug 1361900: Part 9 - Sort scripts by initial load time before saving. r=erahm MozReview-Commit-ID: 54UN2DVK4xM --HG-- extra : rebase_source : f7cb39eb05bb86a03a17b97cbe2cb9d8dd1008fe --- js/xpconnect/loader/ScriptCacheActors.cpp | 3 +- js/xpconnect/loader/ScriptPreloader.cpp | 35 ++++++++++--------- js/xpconnect/loader/ScriptPreloader.h | 41 ++++++++++++++++++++++- 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/js/xpconnect/loader/ScriptCacheActors.cpp b/js/xpconnect/loader/ScriptCacheActors.cpp index 3bd48c9bc43f..b72c58c0b203 100644 --- a/js/xpconnect/loader/ScriptCacheActors.cpp +++ b/js/xpconnect/loader/ScriptCacheActors.cpp @@ -47,6 +47,7 @@ ScriptCacheChild::Finalize(LinkedList& scripts) data->url() = script->mURL; data->cachePath() = script->mCachePath; + data->loadTime() = script->mLoadTime; if (script->HasBuffer()) { auto& xdrData = script->Buffer(); @@ -83,7 +84,7 @@ ScriptCacheParent::Recv__delete__(nsTArray&& scripts) auto& cache = ScriptPreloader::GetChildSingleton(); for (auto& script : scripts) { cache.NoteScript(script.url(), script.cachePath(), processType, - Move(script.xdrData())); + Move(script.xdrData()), script.loadTime()); } return IPC_OK(); diff --git a/js/xpconnect/loader/ScriptPreloader.cpp b/js/xpconnect/loader/ScriptPreloader.cpp index 35756ab650ef..4ecd0e598960 100644 --- a/js/xpconnect/loader/ScriptPreloader.cpp +++ b/js/xpconnect/loader/ScriptPreloader.cpp @@ -498,7 +498,7 @@ ScriptPreloader::InitCacheInternal() for (auto script : mRestoredScripts) { // Only async decode scripts which have been used in this process type. if (script->mProcessTypes.contains(CurrentProcessType()) && - script->mSize > MIN_OFFTHREAD_SIZE && + script->AsyncDecodable() && JS::CanCompileOffThread(cx, options, script->mSize)) { DecodeScriptOffThread(cx, script); } else { @@ -555,8 +555,6 @@ ScriptPreloader::PrepareCacheWrite() AutoSafeJSAPI jsapi; - LinkedList asyncScripts; - for (CachedScript* next = mSavedScripts.getFirst(); next; ) { CachedScript* script = next; next = script->getNext(); @@ -579,20 +577,9 @@ ScriptPreloader::PrepareCacheWrite() delete script; } else { script->mSize = script->Range().length(); - - if (script->mSize > MIN_OFFTHREAD_SIZE) { - script->remove(); - asyncScripts.insertBack(script); - } } } - // Store async-decoded scripts contiguously, since they're loaded - // immediately at startup. - while (CachedScript* s = asyncScripts.popLast()) { - mSavedScripts.insertFront(s); - } - mDataPrepared = true; } @@ -641,9 +628,19 @@ ScriptPreloader::WriteCache() AutoFDClose fd; NS_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget())); + nsTArray scripts; + for (auto script : mSavedScripts) { + scripts.AppendElement(script); + } + + // Sort scripts by load time, with async loaded scripts before sync scripts. + // Since async scripts are always loaded immediately at startup, it helps to + // have them stored contiguously. + scripts.Sort(CachedScript::Comparator()); + OutputBuffer buf; size_t offset = 0; - for (auto script : mSavedScripts) { + for (auto script : scripts) { script->mOffset = offset; script->Code(buf); @@ -656,8 +653,7 @@ ScriptPreloader::WriteCache() MOZ_TRY(Write(fd, MAGIC, sizeof(MAGIC))); MOZ_TRY(Write(fd, headerSize, sizeof(headerSize))); MOZ_TRY(Write(fd, buf.Get(), buf.cursor())); - - for (auto script : mSavedScripts) { + for (auto script : scripts) { MOZ_TRY(Write(fd, script->Range().begin().get(), script->mSize)); if (script->mScript) { @@ -739,12 +735,14 @@ ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath, return; } + script->UpdateLoadTime(TimeStamp::Now()); script->mProcessTypes += CurrentProcessType(); } void ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath, - ProcessType processType, nsTArray&& xdrData) + ProcessType processType, nsTArray&& xdrData, + TimeStamp loadTime) { CachedScript* script = mScripts.Get(cachePath); bool restored = script && FindScript(mRestoredScripts, cachePath); @@ -772,6 +770,7 @@ ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath, } } + script->UpdateLoadTime(loadTime); script->mProcessTypes += processType; } diff --git a/js/xpconnect/loader/ScriptPreloader.h b/js/xpconnect/loader/ScriptPreloader.h index 252d276705ca..c2c2d26b4781 100644 --- a/js/xpconnect/loader/ScriptPreloader.h +++ b/js/xpconnect/loader/ScriptPreloader.h @@ -76,7 +76,8 @@ public: void NoteScript(const nsCString& url, const nsCString& cachePath, JS::HandleScript script); void NoteScript(const nsCString& url, const nsCString& cachePath, - ProcessType processType, nsTArray&& xdrData); + ProcessType processType, nsTArray&& xdrData, + TimeStamp loadTime); // Initializes the script cache from the startup script cache file. Result InitCache(const nsAString& = NS_LITERAL_STRING("scriptCache")); @@ -148,6 +149,33 @@ private: mCache->mScripts.Remove(mCachePath); } + // For use with nsTArray::Sort. + // + // Orders scripts by: + // + // 1) Async-decoded scripts before sync-decoded scripts, since the + // former are needed immediately at startup, and should be stored + // contiguously. + // 2) Script load time, so that scripts which are needed earlier are + // stored earlier, and scripts needed at approximately the same + // time are stored approximately contiguously. + struct Comparator + { + bool Equals(const CachedScript* a, const CachedScript* b) const + { + return (a->AsyncDecodable() == b->AsyncDecodable() && + a->mLoadTime == b->mLoadTime); + } + + bool LessThan(const CachedScript* a, const CachedScript* b) const + { + if (a->AsyncDecodable() != b->AsyncDecodable()) { + return a->AsyncDecodable(); + } + return a->mLoadTime < b->mLoadTime; + } + }; + void Cancel(); void FreeData() @@ -160,6 +188,15 @@ private: } } + void UpdateLoadTime(const TimeStamp& loadTime) + { + if (!mLoadTime || loadTime < mLoadTime) { + mLoadTime = loadTime; + } + } + + bool AsyncDecodable() const { return mSize > MIN_OFFTHREAD_SIZE; } + // Encodes this script into XDR data, and stores the result in mXDRData. // Returns true on success, false on failure. bool XDREncode(JSContext* cx); @@ -237,6 +274,8 @@ private: // The size of this script's encoded XDR data. uint32_t mSize = 0; + TimeStamp mLoadTime{}; + JS::Heap mScript; // True if this script is ready to be executed. This means that either the