HARD-EARNED PERFORMANCE LESSONS SPEED LIMIT +1 David Rajchenbach-Teller (:Yoric) December 2014
OUR NEW STRATEGY 1. Make cool stuff 2. Don t make stuff uncool
LET S DISCUSS PERFORMANCE Responsiveness Speed Battery usage Hangs Crashes
BAD PERFORMANCE IS UNCOOL Knowing Fixing what needs to be fixed is very hard. performance can take years. Regressing performance is nasty. Performance is everybody s job.
1. LET S BUILD A FEATURE WITH PERFORMANCE
DESIGN FOR PERFORMANCE 1. Make it e10s 2. Make it Async 3. Make it Fast 4. Make it a good Layout citizen 5. Avoid killing the battery 6.
IS THAT SUFFICIENT? Hint: no
DON T TRUST YOUR SENSES New features need to work well on mid-range machines.
NOTHING REPLACES TESTING Test on cheaper computers (e.g. Asus T100 Win8 netbooks, Intel GPUs) Test But with and without e10s do not count on Nightly/Aurora/Beta users for performance testing.
NOTHING REPLACES BENCHMARKS Talos is your friend
THINGS TO DO WITH TALOS Check that you re not introducing a regression. If you are adding a smoothness-critical feature, you can add tests. (and we re here to help)
NOTHING REPLACES REALWORLD DATA Telemetry loves you and your dog, too
TELEMETRY (1) "FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS": { "expires_in_version": "never", "kind": "exponential", "high": "30000", "n_buckets": 20, "extended_statistics_ok": true, "description": "Session restore: Time it takes to prepare the data structures for restoring a session (ms)" },
TELEMETRY (2) initsession: function() { TelemetryStopwatch.start( FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS" );! // Do stuff! TelemetryStopwatch.finish( FX_SESSION_RESTORE_STARTUP_INIT_SESSION_MS" ); );
WITH TELEMETRY Already Track regressions semi-automatically. Soon Track user-visible jank. Correlate performance with hardware, add-ons, etc. Correlate performance with user sentiment.
WORKING WITH #PERF During the design phase, we can help: avoiding pitfalls; designing measurements and tests (Telemetry, Talos, target machines); interpreting We data; are here to transfer knowledge!
QUICK RECAP Performance since is your job: the design phase; nothing replaces realistic testing; nothing replaces real-world data (Telemetry). The Performance team is here to help.
WE WILL RETURN AFTER THESE COMMERCIALS QUESTIONS
2. A FEW WORDS ABOUT ASYNC TOOLING
WHAT ABOUT ASYNC TOOLING? Promise & Task; Sqlite.jsm & OS.File; PromiseWorker; AsyncShutdown; Async test; You probably use several of these already.
WRITING ASYNC CODE Use Promise and Task; You may not know it, but you need AsyncShutdown; Expose promiseinitialized from your services, for async startup; Use [Promise]Workers;
AVOID FileUtils and nsifile (use OS.File instead); mozistorage* (use Sqlite.jsm instead) Promise.defer (use PromiseUtils.defer or new Promise()); Uncaught rejections;
BE CAREFUL WITH Launch time of Workers (including OS.File Battery usage of I/O; Cost ಠ_ಠ) ; of communicating between threads; Ill-designed Shutdown. storage back-ends (including schemas);
ASYNC ERROR-HANDLING IS STILL AN OPEN QUESTION
WE WILL RETURN AFTER THESE COMMERCIALS QUESTIONS
3. SHUTDOWN
Shutdown 1. quit-application-requested 2. quit-application-granted 3. quit-application 4. profile-change-net-teardown places-shutdown places-will-closeconnection 5. profile-change-teardown 6. profile-before-change 7. xpcom-will-shutdown profile-beforechange2 xpcom-shutdownthreads Spin Spin Spin 8. xpcom-shutdown web-workersshutdown
What could possibly go wrong?
Async Shutdown
Waiting for clients before shutting down let barrier = new AsyncShutdown.Barrier( My Service will shut down ); let myshutdown = Task.async(function*() { yield barrier.wait(); // Now clean up });
Clients reacting to shutdown MyService.shutdown.addBlocker( I need to finish things before you shutdown, Task.async(function*() { // Finish cleanup }), () => ({ // Report state }) );
The result 1. Clear dependencies.! 2. Crash if you can t shutdown.! 3. No uncontrolled event loop spinning.! 4. Way fewer event loop spinning anyway.! 5. Detailed crash reports.
What s next? 1. Better customisability.! 2. C++ version (landed!)! 3. Maybe, some day, getting rid of our shutdown sequence.! 4. Better instrumentation to track down synchronous freezes during shutdown.
The Terminator
Firefox won t quit
What happened? Misbehaving plug-in or add-on?! Event loop spinning?! Race condition?! Something strange in a static destructor?
The Terminator Shutdown watchdog.! Starts at the first sign of shutdown.! Ensures that shutdown is progressing.! After ~60s without progress, crash.
What s next? 1. Crash stats.! 2. Telemetry.! 3. Killing without crashing.
WE WILL RETURN AFTER THESE COMMERCIALS QUESTIONS
4. ERROR-HANDLING
CONSOLE WARNINGS [72767] WARNING: '!compmgr', file /Users/david/Documents/Code/mc-telemetry/xpcom/glue/nsComponentManagerUtils.cpp, line 63 [72767] WARNING: dependent window created without a parent: file /Users/david/Documents/Code/mc-telemetry/toolkit/components/startup/ nsappstartup.cpp, line 666 ++DOCSHELL 0x115a3e800 == 1 [pid = 72767] [id = 1] ++DOMWINDOW == 1 (0x115a1f000) [pid = 72767] [serial = 1] [outer = 0x0] ++DOMWINDOW == 2 (0x115a1ac00) [pid = 72767] [serial = 2] [outer = 0x115a1f000] [72767] WARNING: NS_ENSURE_SUCCESS(rv, nsresult::ns_error_unexpected) failed with result 0x80004005: file /Users/david/Documents/Code/mctelemetry/extensions/cookie/nsPermissionManager.cpp, line 464 JavaScript error: file:///users/david/documents/code/mc-telemetry/obj-x86_64-apple-darwin13.4.0.noindex/dist/nightlydebug.app/contents/ Resources/components/nsHandlerService.js, line 891: NS_ERROR_FAILURE: Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsiproperties.get] JavaScript error: file:///users/david/documents/code/mc-telemetry/obj-x86_64-apple-darwin13.4.0.noindex/dist/nightlydebug.app/contents/ Resources/components/XULStore.js, line 66: NS_ERROR_FAILURE: Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsiproperties.get] [72767] WARNING: '!mlocalstore', file /Users/david/Documents/Code/mc-telemetry/dom/xul/XULDocument.cpp, line 2071 [72767] WARNING: NS_ENSURE_TRUE(startupCache) failed: file /Users/david/Documents/Code/mc-telemetry/dom/xbl/nsXBLDocumentInfo.cpp, line 237!! JavaScript strict warning: resource://gre/modules/appsutils.jsm, line 760: ReferenceError: reference to undefined property this._manifest[aprop]!! [72768] WARNING: Loaded script chrome://browser/content/places/browserplacesviews.js twice (bug 392650): file /Users/david/Documents/Code/ mc-telemetry/dom/xul/nsxulprototypecache.cpp, line 216 JavaScript strict warning:, line 0: TypeError: setting a property that has only a getter JavaScript strict warning:, line 0: TypeError: setting a property that has only a getter JavaScript strict warning:, line 0: TypeError: setting a property that has only a getter JavaScript strict warning:, line 0: TypeError: setting a property that has only a getter! [Parent 72768] ###!!! ASSERTION: Received an invalid TabContext from the child process. (Got an ownappid that didn't correspond to an app.): 'Error', file /Users/david/Documents/Code/mc-telemetry/dom/ipc/ContentProcessManager.cpp, line 179! JavaScript strict warning: file:///users/david/documents/code/mc-telemetry/obj-x86_64-apple-darwin13.4.0.noindex/dist/nightlydebug.app/ Contents/Resources/components/nsSearchService.js, line 3384: ReferenceError: reference to undefined property json._datatype
BROWSER CONSOLE WARNINGS Could not read chrome manifest 'file:///applications/firefoxnightly.app/contents/resources/chrome.manifest'.! JavaScript 1.7's let blocks are deprecated adb.js:810:79! JavaScript 1.7's let blocks are deprecated adb.js:838:52! Could not read chrome manifest 'file:///users/david/library/application%20support/firefox/profiles/l5suw4b1.default/extensions/fr-classiquereforme1990@dictionaries.addons.mozilla.org/chrome.manifest'.! Could not read chrome manifest 'file:///applications/firefoxnightly.app/contents/resources/browser/extensions/%7b972ce4c6-7e08-4474-a285-3208198ce6fd%7d/chrome.manifest'.! Warning: unrecognized command line flag -foreground! nsbrowsercontenthandler.js:670:0! 1417705754468! Services.HealthReport.HealthReporter! WARN! No prefs data found.! OpenGL compositor Initialized Succesfully.! Version: 2.1 INTEL-8.28.32! Vendor: Intel Inc.! Renderer: Intel Iris OpenGL Engine! FBO Texture Target: TEXTURE_2D! SessionHistory: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsiobjectinputstream.readobject]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: resource://app/modules/sessionstore/sessionhistory.jsm :: SessionHistoryInternal.deserializeEntry :: line 417" data: no]! JavaScript 1.7's let blocks are deprecated clipboard.js:129:6! JavaScript 1.7's let blocks are deprecated clipboard.js:140:6! mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create sync.js:112:4! Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help http://xhr.spec.whatwg.org/ utils.js:37:0! not well-formed messages.json:1:1! downloadable font: maxp: bad max_zones: 0 (font-family: "FiraSans" style:normal weight:normal stretch:normal src index:1) source: resource://id-at-baku-dot-switchy/switchy/ data/fonts/firasans-light.woff fira.css:12:12! downloadable font: maxp: bad max_zones: 0 (font-family: "FiraSans" style:italic weight:bold stretch:normal src index:1) source: resource://id-at-baku-dot-switchy/switchy/ data/fonts/firasans-italic.woff fira.css:1:12! downloadable font: OS/2: bad linegap: -32 (font-family: "social" style:normal weight:normal stretch:normal src index:1) source: resource://id-at-baku-dot-switchy/switchy/ data/fonts/social.woff?-3o6kus icons.css:1:12! downloadable font: maxp: bad max_zones: 0 (font-family: "FiraSans" style:normal weight:bold stretch:normal src index:1) source: resource://id-at-baku-dot-switchy/switchy/ data/fonts/firasans-regular.woff fira.css:34:12! Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://bugzilla.mozilla.org/rest/bug? status=new&status=unconfirmed&status=assigned&status=reopened&f1=requestees.login_name&o1=substring&v1=dteller%40mozilla.com&include_fields=id%2cpriority%2cseverity %2Csummary%2Cstatus%2Ccomponent%2Cassignee%2Clast_change_time&changed_after=2014-08-28T15%3A09%3A37Z. This can be fixed by moving the resource to the same domain or enabling CORS. bug! NS_ERROR_NOT_INITIALIZED: Component returned failure code: 0xc1f30001 (NS_ERROR_NOT_INITIALIZED) [nsimessagesender.sendasyncmessage] BackgroundPageThumbs.jsm:298:0! Key event not available on some keyboard layouts: key="g" modifiers="alt control" browser.xul! 1417705784478! Services.Metrics.Provider.org.mozilla.addons! WARN! Add-on type without field: experiment! 1417705784530! Services.Metrics.Provider.org.mozilla.addons! WARN! Add-on type without field: experiment! OpenGL compositor Initialized Succesfully.! Version: 2.1 INTEL-8.28.32! Vendor: Intel Inc.! Renderer: Intel Iris OpenGL Engine! FBO Texture Target: TEXTURE_2D! NS_ERROR_NOT_INITIALIZED: Component returned failure code: 0xc1f30001 (NS_ERROR_NOT_INITIALIZED) [nsimessagesender.sendasyncmessage] BackgroundPageThumbs.jsm:298:0! "
REPORTING ERRORS? try { throw new TypeError( Expected a number ); } catch (ex) { Cu.reportError(ex); }
HOW DO WE USE THESE? Is the developer informed when there is a warning? Is there any actionable data? Is there anything useful?
OUR ERROR-HANDLING IS BROKEN And uncool, too.
UNBREAKING ERRORS Errors Test must reach the developers suites FHR? Errors No must be actionable error without a line number (preferably a stack)
A POSSIBLE API Warning.fail(category, error[, stack])! MOZ_WARNING_FAIL(category, error) Replaces Cu.reportError and NS_WARNING Causes all test suites to fail, with stack and message. Logged to FHR, with stack and message.
TESTSUITE-SIDE API Assert.whitelist.FIXME(category, regexp) // The test shouldn t cause this error, // investigation in progress.! Assert.whitelist.expected(category, regexp) // This error is normal in the test.
EXAMPLE statement.executeasync({ //... handleerror: function(aerror) { Warning.fail( ChromeProfileMigrator.GetHistoryResource, Async statement execution failed, aerror); } });
READY? SET? ASK QUESTIONS!
That s all Folks