startup perf: Reduce require() calls

Every require() that we do before the users sees UI reduces startup
time.

I used the following code (added to index.js) to log every require()
call in the main process:

```js
var Module = require('module')
var required = {}
Module.prototype.require = function (orig) {
  return function (id) {
    if (!required[id]) {
      required[id] = true
      console.log(`${id}   (from ${this.filename})`)
    }
    return orig.apply(this, arguments)
  }
}(Module.prototype.require)
```

From this I was able to learn that lots of modules were being required
that aren't actually used until later.

I also sent this related PR to eliminate another few require()s:
https://github.com/LinusU/node-application-config/pull/4

This increases startup time by 50ms.

We'll probably realize much bigger gains by following this same
procedure for the renderer process.
This commit is contained in:
Feross Aboukhadijeh
2016-09-22 16:33:26 -07:00
parent b8bdf65514
commit a08d576851
6 changed files with 119 additions and 49 deletions

View File

@@ -2,23 +2,15 @@ console.time('init')
const electron = require('electron')
const app = electron.app
const ipcMain = electron.ipcMain
const parallel = require('run-parallel')
const announcement = require('./announcement')
const config = require('../config')
const crashReporter = require('../crash-reporter')
const dialog = require('./dialog')
const dock = require('./dock')
const ipc = require('./ipc')
const log = require('./log')
const menu = require('./menu')
const squirrelWin32 = require('./squirrel-win32')
const State = require('../renderer/lib/state')
const tray = require('./tray')
const updater = require('./updater')
const userTasks = require('./user-tasks')
const windows = require('./windows')
let shouldQuit = false
@@ -36,6 +28,7 @@ if (config.IS_PRODUCTION) {
}
if (process.platform === 'win32') {
const squirrelWin32 = require('./squirrel-win32')
shouldQuit = squirrelWin32.handleEvent(argv[0])
argv = argv.filter((arg) => !arg.includes('--squirrel'))
}
@@ -58,6 +51,8 @@ function init () {
app.setPath('userData', config.CONFIG_PATH)
}
const ipcMain = electron.ipcMain
let isReady = false // app ready, windows can be created
app.ipcReady = false // main window has finished loading and IPC is ready
app.isQuitting = false
@@ -121,6 +116,12 @@ function init () {
}
function delayedInit () {
const announcement = require('./announcement')
const dock = require('./dock')
const tray = require('./tray')
const updater = require('./updater')
const userTasks = require('./user-tasks')
announcement.init()
dock.init()
tray.init()
@@ -169,12 +170,16 @@ function sliceArgv (argv) {
function processArgv (argv) {
let torrentIds = []
argv.forEach(function (arg) {
if (arg === '-n') {
dialog.openSeedDirectory()
} else if (arg === '-o') {
dialog.openTorrentFile()
} else if (arg === '-u') {
dialog.openTorrentAddress()
if (arg === '-n' || arg === '-o' || arg === '-u') {
// Critical path: Only load the 'dialog' package if it is needed
const dialog = require('./dialog')
if (arg === '-n') {
dialog.openSeedDirectory()
} else if (arg === '-o') {
dialog.openTorrentFile()
} else if (arg === '-u') {
dialog.openTorrentAddress()
}
} else if (arg === '--hidden') {
// Ignore hidden argument, already being handled
} else if (arg.startsWith('-psn')) {

View File

@@ -6,18 +6,9 @@ const electron = require('electron')
const app = electron.app
const dialog = require('./dialog')
const dock = require('./dock')
const handlers = require('./handlers')
const log = require('./log')
const menu = require('./menu')
const powerSaveBlocker = require('./power-save-blocker')
const shell = require('./shell')
const shortcuts = require('./shortcuts')
const externalPlayer = require('./external-player')
const windows = require('./windows')
const thumbar = require('./thumbar')
const startup = require('./startup')
// Messages from the main process, to be sent once the WebTorrent process starts
const messageQueueMainToWebTorrent = []
@@ -44,21 +35,37 @@ function init () {
* Dialog
*/
ipc.on('openTorrentFile', () => dialog.openTorrentFile())
ipc.on('openFiles', () => dialog.openFiles())
ipc.on('openTorrentFile', () => {
const dialog = require('./dialog')
dialog.openTorrentFile()
})
ipc.on('openFiles', () => {
const dialog = require('./dialog')
dialog.openFiles()
})
/**
* Dock
*/
ipc.on('setBadge', (e, ...args) => dock.setBadge(...args))
ipc.on('downloadFinished', (e, ...args) => dock.downloadFinished(...args))
ipc.on('setBadge', (e, ...args) => {
const dock = require('./dock')
dock.setBadge(...args)
})
ipc.on('downloadFinished', (e, ...args) => {
const dock = require('./dock')
dock.downloadFinished(...args)
})
/**
* Events
*/
ipc.on('onPlayerOpen', function () {
const powerSaveBlocker = require('./power-save-blocker')
const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar')
menu.togglePlaybackControls(true)
powerSaveBlocker.enable()
shortcuts.enable()
@@ -66,11 +73,17 @@ function init () {
})
ipc.on('onPlayerUpdate', function (e, ...args) {
const thumbar = require('./thumbar')
menu.onPlayerUpdate(...args)
thumbar.onPlayerUpdate(...args)
})
ipc.on('onPlayerClose', function () {
const powerSaveBlocker = require('./power-save-blocker')
const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar')
menu.togglePlaybackControls(false)
powerSaveBlocker.disable()
shortcuts.disable()
@@ -78,11 +91,17 @@ function init () {
})
ipc.on('onPlayerPlay', function () {
const powerSaveBlocker = require('./power-save-blocker')
const thumbar = require('./thumbar')
powerSaveBlocker.enable()
thumbar.onPlayerPlay()
})
ipc.on('onPlayerPause', function () {
const powerSaveBlocker = require('./power-save-blocker')
const thumbar = require('./thumbar')
powerSaveBlocker.disable()
thumbar.onPlayerPause()
})
@@ -91,15 +110,26 @@ function init () {
* Shell
*/
ipc.on('openItem', (e, ...args) => shell.openItem(...args))
ipc.on('showItemInFolder', (e, ...args) => shell.showItemInFolder(...args))
ipc.on('moveItemToTrash', (e, ...args) => shell.moveItemToTrash(...args))
ipc.on('openItem', (e, ...args) => {
const shell = require('./shell')
shell.openItem(...args)
})
ipc.on('showItemInFolder', (e, ...args) => {
const shell = require('./shell')
shell.showItemInFolder(...args)
})
ipc.on('moveItemToTrash', (e, ...args) => {
const shell = require('./shell')
shell.moveItemToTrash(...args)
})
/**
* File handlers
*/
ipc.on('setDefaultFileHandler', (e, flag) => {
const handlers = require('./handlers')
if (flag) handlers.install()
else handlers.uninstall()
})
@@ -109,6 +139,8 @@ function init () {
*/
ipc.on('setStartup', (e, flag) => {
const startup = require('./startup')
if (flag) startup.install()
else startup.uninstall()
})
@@ -132,18 +164,26 @@ function init () {
*/
ipc.on('checkForExternalPlayer', function (e, path) {
const externalPlayer = require('./external-player')
externalPlayer.checkInstall(path, function (isInstalled) {
windows.main.send('checkForExternalPlayer', isInstalled)
})
})
ipc.on('openExternalPlayer', (e, ...args) => {
const externalPlayer = require('./external-player')
const thumbar = require('./thumbar')
menu.togglePlaybackControls(false)
thumbar.disable()
externalPlayer.spawn(...args)
})
ipc.on('quitExternalPlayer', () => externalPlayer.kill())
ipc.on('quitExternalPlayer', () => {
const externalPlayer = require('./external-player')
externalPlayer.kill()
})
/**
* Message passing

View File

@@ -13,8 +13,6 @@ const electron = require('electron')
const app = electron.app
const config = require('../config')
const dialog = require('./dialog')
const shell = require('./shell')
const windows = require('./windows')
let menu = null
@@ -90,17 +88,26 @@ function getMenuTemplate () {
? 'Create New Torrent...'
: 'Create New Torrent from Folder...',
accelerator: 'CmdOrCtrl+N',
click: () => dialog.openSeedDirectory()
click: () => {
const dialog = require('./dialog')
dialog.openSeedDirectory()
}
},
{
label: 'Open Torrent File...',
accelerator: 'CmdOrCtrl+O',
click: () => dialog.openTorrentFile()
click: () => {
const dialog = require('./dialog')
dialog.openTorrentFile()
}
},
{
label: 'Open Torrent Address...',
accelerator: 'CmdOrCtrl+U',
click: () => dialog.openTorrentAddress()
click: () => {
const dialog = require('./dialog')
dialog.openTorrentAddress()
}
},
{
type: 'separator'
@@ -277,18 +284,27 @@ function getMenuTemplate () {
submenu: [
{
label: 'Learn more about ' + config.APP_NAME,
click: () => shell.openExternal(config.HOME_PAGE_URL)
click: () => {
const shell = require('./shell')
shell.openExternal(config.HOME_PAGE_URL)
}
},
{
label: 'Contribute on GitHub',
click: () => shell.openExternal(config.GITHUB_URL)
click: () => {
const shell = require('./shell')
shell.openExternal(config.GITHUB_URL)
}
},
{
type: 'separator'
},
{
label: 'Report an Issue...',
click: () => shell.openExternal(config.GITHUB_URL_ISSUES)
click: () => {
const shell = require('./shell')
shell.openExternal(config.GITHUB_URL_ISSUES)
}
}
]
}
@@ -361,7 +377,10 @@ function getMenuTemplate () {
// File menu (Windows, Linux)
template[0].submenu.unshift({
label: 'Create New Torrent from File...',
click: () => dialog.openSeedFile()
click: () => {
const dialog = require('./dialog')
dialog.openSeedFile()
}
})
// Edit menu (Windows, Linux)

View File

@@ -21,7 +21,6 @@ const app = electron.app
const config = require('../../config')
const log = require('../log')
const menu = require('../menu')
const tray = require('../tray')
function init (state, options) {
if (main.win) {
@@ -81,6 +80,8 @@ function init (state, options) {
})
win.on('close', function (e) {
const tray = require('../tray')
if (process.platform !== 'darwin' && !tray.hasTray()) {
app.quit()
} else if (!app.isQuitting) {
@@ -221,11 +222,15 @@ function toggleFullScreen (flag) {
}
function onWindowBlur () {
const tray = require('../tray')
menu.setWindowFocus(false)
tray.setWindowFocus(false)
}
function onWindowFocus () {
const tray = require('../tray')
menu.setWindowFocus(true)
tray.setWindowFocus(true)
}

View File

@@ -4,10 +4,10 @@ module.exports = {
run
}
const semver = require('semver')
const config = require('../../config')
const TorrentSummary = require('./torrent-summary')
const fs = require('fs')
const semver = require('semver')
const config = require('../../config')
// Change `state.saved` (which will be saved back to config.json on exit) as
// needed, for example to deal with config.json format changes across versions
@@ -116,6 +116,8 @@ function migrate_0_11_0 (saved) {
}
function migrate_0_12_0 (saved) {
const TorrentSummary = require('./torrent-summary')
if (saved.prefs.openExternalPlayer == null && saved.prefs.playInVlc != null) {
saved.prefs.openExternalPlayer = saved.prefs.playInVlc
}

View File

@@ -181,19 +181,18 @@ function getExternalPlayerName () {
}
function load (cb) {
const state = getDefaultState()
appConfig.read(function (err, saved) {
if (err || !saved.version) {
console.log('Missing config file: Creating new one')
setupStateSaved(onSaved)
setupStateSaved(onSavedState)
} else {
onSaved(null, saved)
onSavedState(null, saved)
}
})
function onSaved (err, saved) {
function onSavedState (err, saved) {
if (err) return cb(err)
const state = getDefaultState()
state.saved = saved
migrations.run(state)
cb(null, state)