-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create new event with emitter object to simplify code #4166
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This def. looks easier to use 👍
Yes perf would be my main concern here for these reasons:
- heavy getter usage (prolly not an issue anymore with ES2015?)
- heavy "closured" code, e.g. binding of
Emitter.fire
andEmitter.dispose
ontoEmitter.event
capturingEmitter
3 times (not even sure if GC can cleanup this fast asEmitter
is captured circular onEmitter.event
?)
I remember older browser engines to have issues with GC and circular refs, imho this needs a memory burn test from millions of events and whether dispose
still gets them cleaned up. Also perf-wise - prolly needs tests on different engines (no clue how Firefox keeps up with that).
Lastly I think this approach makes the Emitter itself unreachable from code symbols - during some debug tests way back I needed to inspect the listeners, which is now not possible anymore?
At this point I wonder, if a more flat code structure as some sort of Event
class still giving access to emitter internals would avoid all those issues/concerns?
Yes I don't think the getter thing is a problem in newer TS targets, but also this isn't a getting but a regular value property (
I'll try test
I exposed
We could have done this before by just exposing |
Some tests below, it seems as good as or better based on the results. Provided the tests are doing the right thing 🙂 Init + listen to 1 million events performance Before: private _testEvents(): void {
const emitter = new EventEmitter<string>();
const event = emitter.event;
for (let i = 0; i < 1000000; i++) {
event(e => e);
}
} After: private _testEvents(): void {
const ev = initEvent<string>();
for (let i = 0; i < 1000000; i++) {
ev(e => e);
}
} Init + listen to 1 million events + fire performance Before: private _testEvents(): void {
const emitter = new EventEmitter<string>();
const event = emitter.event;
for (let i = 0; i < 1000000; i++) {
event(e => e);
}
emitter.fire('foo');
} After: private _testEvents(): void {
const ev = initEvent<string>();
for (let i = 0; i < 1000000; i++) {
ev(e => e);
}
ev.fire('foo');
} Memory after forced GC without dispose Before: private _emitter: IEventEmitter<string> | undefined = new EventEmitter<string>();
private _testEvents(): void {
const event = this._emitter!.event;
for (let i = 0; i < 1000000; i++) {
event!(e => e);
}
this._emitter!.fire('foo');
} Heap snapshot: 52.6MB After: private _ev = initEvent<string>();
private _testEvents(): void {
for (let i = 0; i < 1000000; i++) {
this._ev(e => e);
}
this._ev.fire('foo');
} Heap snapshot: 45.3MB Memory after forced GC with dispose Before: private _emitter: IEventEmitter<string> | undefined = new EventEmitter<string>();
private _testEvents(): void {
let event: IEvent<string> | undefined = this._emitter!.event;
for (let i = 0; i < 1000000; i++) {
event!(e => e);
}
this._emitter!.fire('foo');
setTimeout(() => {
this._emitter!.dispose();
this._emitter = undefined;
event = undefined;
}, 1000);
} Heap snapshot: ~23-28MB After: private _ev: IEventWithEmitter<string> | undefined = initEvent<string>();
private _testEvents(): void {
for (let i = 0; i < 1000000; i++) {
this._ev!(e => e);
}
this._ev!.fire('foo');
setTimeout(() => {
this._ev!.dispose();
this._ev = undefined;
}, 1000);
} Heap snapshot: 15.7MB |
Yepp, looks better than the old variant 👍. Not sure what magic they use to spot unreachable cycling refs in GC w'o perf hit, maybe they can mark whole cycles as unreachable pretty fast w'o going into bad complexity from expensive graph algos. |
Will merge then, we can always tweak later on if there are problems. So much nicer to write and read like this |
- Bump copyright year xtermjs/xterm.js#4176 - Share texture atlas cache code between webgl and canvas renderers xtermjs/xterm.js#4170 - Add willReadFrequently to canvas renderer too xtermjs/xterm.js#4169 - Ensure texture atlas comparison uses rgba not object xtermjs/xterm.js#4168 - Create new event with emitter object to simplify code xtermjs/xterm.js#4166 - Define all events and emitters consistently xtermjs/xterm.js#4165 - Inline dirty row service into input handler xtermjs/xterm.js#4163 - Move w objects to $ prefix variables xtermjs/xterm.js#4162 Fixes #158984 Fixes #158874
Revert "Merge pull request #4166 from Tyriar/event_with_emitter"
- Bump copyright year xtermjs/xterm.js#4176 - Share texture atlas cache code between webgl and canvas renderers xtermjs/xterm.js#4170 - Add willReadFrequently to canvas renderer too xtermjs/xterm.js#4169 - Ensure texture atlas comparison uses rgba not object xtermjs/xterm.js#4168 - Create new event with emitter object to simplify code xtermjs/xterm.js#4166 - Define all events and emitters consistently xtermjs/xterm.js#4165 - Inline dirty row service into input handler xtermjs/xterm.js#4163 - Move w objects to $ prefix variables xtermjs/xterm.js#4162 Fixes microsoft#158984 Fixes microsoft#158874
@jerch thoughts? I think this is pretty neat 🙂
Runtime performance seems the same