Add perf optimizations (#1541)

* pass `uid` to term

* term: keep track of term instance references

* sessions: remvoe `write` object overhead

* hterm: cache measurement of codepoints for single char strings

* sessions: merge less eagerly when we receive a PTY_DATA action

* store: handle the side effect of writing to the terminal from a middleware

* terms: add terms instance cache

* lint
This commit is contained in:
Guillermo Rauch 2017-02-23 03:10:05 -08:00 committed by Matheus Fernandes
parent 1cd2620da0
commit dc3b90028b
9 changed files with 54 additions and 26 deletions

View file

@ -76,7 +76,8 @@ class TermGroup_ extends Component {
onData: this.bind(this.props.onData, null, uid),
onURLAbort: this.bind(this.props.onURLAbort, null, uid),
borderColor: this.props.borderColor,
quickEdit: this.props.quickEdit
quickEdit: this.props.quickEdit,
uid
});
// This will create a new ref_ function for every render,

View file

@ -5,6 +5,7 @@ import uuid from 'uuid';
import hterm from '../hterm';
import Component from '../component';
import getColorList from '../utils/colors';
import terms from '../terms';
import notify from '../utils/notify';
export default class Term extends Component {
@ -97,6 +98,7 @@ export default class Term extends Component {
this.getScreenNode().addEventListener('mouseup', this.handleMouseUp);
this.getScreenNode().addEventListener('mousedown', this.handleMouseDown);
terms[this.props.uid] = this;
}
handleWheel(e) {
@ -364,6 +366,7 @@ export default class Term extends Component {
}
componentWillUnmount() {
terms[this.props.uid] = this;
// turn blinking off to prevent leaking a timeout when disposing terminal
const prefs = this.term.getPrefs();
prefs.set('cursor-blink', false);

View file

@ -16,13 +16,6 @@ export default class Terms extends Component {
props.ref_(this);
}
componentWillReceiveProps(next) {
const {write} = next;
if (write && this.props.write !== write) {
this.getTermByUid(write.uid).write(write.data);
}
}
shouldComponentUpdate(nextProps) {
for (const i in nextProps) {
if (i === 'write') {

View file

@ -57,7 +57,12 @@ hterm.TextAttributes.splitWidecharString = function (str) {
};
// hterm Unicode patch
const cache = [];
lib.wc.strWidth = function (str) {
const shouldCache = str.length === 1;
if (shouldCache && cache[str] !== undefined) {
return cache[str];
}
const chars = runes(str);
let width = 0;
let rv = 0;
@ -70,6 +75,9 @@ lib.wc.strWidth = function (str) {
}
rv += width * ((codePoint <= 0xffff) ? 1 : 2);
}
if (shouldCache) {
cache[str] = rv;
}
return rv;
};

View file

@ -16,7 +16,6 @@ import {
const initialState = Immutable({
sessions: {},
write: null,
activeUid: null
});
@ -26,7 +25,6 @@ function Session(obj) {
title: '',
cols: null,
rows: null,
write: null,
url: null,
cleared: false,
shell: '',
@ -34,13 +32,6 @@ function Session(obj) {
}).merge(obj);
}
function Write(obj) {
return Immutable({
uid: '',
data: ''
}).merge(obj);
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case SESSION_ADD:
@ -73,8 +64,11 @@ const reducer = (state = initialState, action) => {
}, {deep: true});
case SESSION_PTY_DATA:
// we avoid a direct merge for perf reasons
// as this is the most common action
if (state.sessions[action.uid] &&
state.sessions[action.uid].cleared) {
return state
.set('write', Write(action))
.merge({
sessions: {
[action.uid]: {
@ -82,6 +76,8 @@ const reducer = (state = initialState, action) => {
}
}
}, {deep: true});
}
return state;
case SESSION_PTY_EXIT:
if (state.sessions[action.uid]) {

View file

@ -4,6 +4,7 @@ import createLogger from 'redux-logger';
import rootReducer from '../reducers/index';
import effects from '../utils/effects';
import * as plugins from '../utils/plugins';
import writeMiddleware from './write-middleware';
export default () => {
const logger = createLogger({
@ -17,6 +18,7 @@ export default () => {
plugins.middleware,
thunk,
effects,
writeMiddleware,
logger
),
window.devToolsExtension()

View file

@ -3,6 +3,7 @@ import thunk from 'redux-thunk';
import rootReducer from '../reducers/index';
import effects from '../utils/effects';
import * as plugins from '../utils/plugins';
import writeMiddleware from './write-middleware';
export default () =>
createStore(
@ -11,6 +12,7 @@ export default () =>
thunk,
plugins.middleware,
thunk,
effects
effects,
writeMiddleware
)
);

View file

@ -0,0 +1,14 @@
import terms from '../terms';
// the only side effect we perform from middleware
// is to write to the react term instance directly
// to avoid a performance hit
export default () => next => action => {
if (action.type === 'SESSION_PTY_DATA') {
const term = terms[action.uid];
if (term) {
term.write(action.data);
}
}
next(action);
};

9
lib/terms.js Normal file
View file

@ -0,0 +1,9 @@
// react Term components add themselves
// to this object upon mounting / unmounting
// this is to allow imperative access to the
// term API, which is a performance
// optimization for the most common action
// within the system
const terms = {};
export default terms;