Skip to content

Commit 3f659a1

Browse files
committed
minor fixes:
- better perf for short DCS without params (8 times faster) - test perf for class and string based interfaces of OSC and DCS separately - undo wrong commit on parser
1 parent dd5b295 commit 3f659a1

File tree

3 files changed

+105
-43
lines changed

3 files changed

+105
-43
lines changed

src/common/parser/DcsParser.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,27 @@ export class DcsParser implements IDcsParser {
100100
}
101101
}
102102

103+
// predefine empty params as [0] (ZDM)
104+
const EMPTY_PARAMS = new Params();
105+
EMPTY_PARAMS.addParam(0);
106+
103107
/**
104108
* Convenient class to create a DCS handler from a single callback function.
105109
* Note: The payload is currently limited to 50 MB (hardcoded).
106110
*/
107111
export class DcsHandler implements IDcsHandler {
108112
private _data = '';
109-
private _params: IParams | undefined;
113+
private _params: IParams = EMPTY_PARAMS;
110114
private _hitLimit: boolean = false;
111115

112116
constructor(private _handler: (data: string, params: IParams) => boolean) {}
113117

114118
public hook(params: IParams): void {
115-
this._params = params.clone();
119+
// since we need to preserve params until `unhook`, we have to clone it
120+
// (only borrowed from parser and spans multiple parser states)
121+
// perf optimization:
122+
// clone only, if we have non empty params, otherwise stick with default
123+
this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS;
116124
this._data = '';
117125
this._hitLimit = false;
118126
}
@@ -133,9 +141,9 @@ export class DcsHandler implements IDcsHandler {
133141
if (this._hitLimit) {
134142
ret = false;
135143
} else if (success) {
136-
ret = this._handler(this._data, this._params || new Params());
144+
ret = this._handler(this._data, this._params);
137145
}
138-
this._params = undefined;
146+
this._params = EMPTY_PARAMS;
139147
this._data = '';
140148
this._hitLimit = false;
141149
return ret;

src/common/parser/EscapeSequenceParser.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -582,21 +582,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
582582
i = j - 1;
583583
break;
584584
}
585-
if (++j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
586-
dcs.put(data, i, j);
587-
i = j - 1;
588-
break;
589-
}
590-
if (++j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
591-
dcs.put(data, i, j);
592-
i = j - 1;
593-
break;
594-
}
595-
if (++j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
596-
dcs.put(data, i, j);
597-
i = j - 1;
598-
break;
599-
}
600585
}
601586
break;
602587
case ParserAction.DCS_UNHOOK:
@@ -613,7 +598,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
613598
case ParserAction.OSC_PUT:
614599
// inner loop: 0x20 (SP) included, 0x7F (DEL) included
615600
for (let j = i + 1; ; j++) {
616-
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code <= 0x9f)) {
601+
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
617602
osc.put(data, i, j);
618603
i = j - 1;
619604
break;

test/benchmark/EscapeSequenceParser.benchmark.ts

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import { perfContext, before, beforeEach, ThroughputRuntimeCase } from 'xterm-be
66

77
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
88
import { C0, C1 } from 'common/data/EscapeSequences';
9-
import { IDcsHandler, IParams } from 'common/parser/Types';
9+
import { IDcsHandler, IOscHandler, IParams } from 'common/parser/Types';
1010
import { OscHandler } from 'common/parser/OscParser';
11+
import { DcsHandler } from '../../out/common/parser/DcsParser';
1112

13+
const SIZE = 5000000;
1214

1315
function toUtf32(s: string): Uint32Array {
1416
const result = new Uint32Array(s.length);
@@ -18,10 +20,16 @@ function toUtf32(s: string): Uint32Array {
1820
return result;
1921
}
2022

21-
class DcsHandler implements IDcsHandler {
23+
class FastDcsHandler implements IDcsHandler {
2224
public hook(params: IParams): void {}
2325
public put(data: Uint32Array, start: number, end: number): void {}
24-
public unhook(): boolean { return true; }
26+
public unhook(success: boolean): boolean { return true; }
27+
}
28+
29+
class FastOscHandler implements IOscHandler {
30+
public start(): void {}
31+
public put(data: Uint32Array, start: number, end: number): void {}
32+
public end(success: boolean): boolean { return true; }
2533
}
2634

2735

@@ -81,7 +89,7 @@ perfContext('Parser throughput - 50MB data', () => {
8189
parser.setExecuteHandler(C1.NEL, () => true);
8290
parser.setExecuteHandler(C1.HTS, () => true);
8391
parser.registerOscHandler(0, new OscHandler(data => true));
84-
parser.registerOscHandler(2, new OscHandler(data => true));
92+
parser.registerOscHandler(1, new FastOscHandler());
8593
parser.registerEscHandler({final: '7'}, () => true);
8694
parser.registerEscHandler({final: '8'}, () => true);
8795
parser.registerEscHandler({final: 'D'}, () => true);
@@ -98,14 +106,15 @@ perfContext('Parser throughput - 50MB data', () => {
98106
parser.registerEscHandler({final: '~'}, () => true);
99107
parser.registerEscHandler({intermediates: '%', final: '@'}, () => true);
100108
parser.registerEscHandler({intermediates: '%', final: 'G'}, () => true);
101-
parser.registerDcsHandler({final: 'q'}, new DcsHandler());
109+
parser.registerDcsHandler({final: 'p'}, new DcsHandler(data => true));
110+
parser.registerDcsHandler({final: 'q'}, new FastDcsHandler());
102111
});
103112

104113
perfContext('PRINT - a', () => {
105114
before(() => {
106115
const data = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
107116
let content = '';
108-
while (content.length < 50000000) {
117+
while (content.length < SIZE) {
109118
content += data;
110119
}
111120
parsed = toUtf32(content);
@@ -120,7 +129,7 @@ perfContext('Parser throughput - 50MB data', () => {
120129
before(() => {
121130
const data = '\n\n\n\n\n\n\n';
122131
let content = '';
123-
while (content.length < 50000000) {
132+
while (content.length < SIZE) {
124133
content += data;
125134
}
126135
parsed = toUtf32(content);
@@ -135,7 +144,7 @@ perfContext('Parser throughput - 50MB data', () => {
135144
before(() => {
136145
const data = '\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE';
137146
let content = '';
138-
while (content.length < 50000000) {
147+
while (content.length < SIZE) {
139148
content += data;
140149
}
141150
parsed = toUtf32(content);
@@ -150,7 +159,7 @@ perfContext('Parser throughput - 50MB data', () => {
150159
before(() => {
151160
const data = '\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G';
152161
let content = '';
153-
while (content.length < 50000000) {
162+
while (content.length < SIZE) {
154163
content += data;
155164
}
156165
parsed = toUtf32(content);
@@ -165,7 +174,7 @@ perfContext('Parser throughput - 50MB data', () => {
165174
before(() => {
166175
const data = '\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A';
167176
let content = '';
168-
while (content.length < 50000000) {
177+
while (content.length < SIZE) {
169178
content += data;
170179
}
171180
parsed = toUtf32(content);
@@ -180,7 +189,7 @@ perfContext('Parser throughput - 50MB data', () => {
180189
before(() => {
181190
const data = '\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p';
182191
let content = '';
183-
while (content.length < 50000000) {
192+
while (content.length < SIZE) {
184193
content += data;
185194
}
186195
parsed = toUtf32(content);
@@ -195,7 +204,7 @@ perfContext('Parser throughput - 50MB data', () => {
195204
before(() => {
196205
const data = '\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m';
197206
let content = '';
198-
while (content.length < 50000000) {
207+
while (content.length < SIZE) {
199208
content += data;
200209
}
201210
parsed = toUtf32(content);
@@ -210,7 +219,7 @@ perfContext('Parser throughput - 50MB data', () => {
210219
before(() => {
211220
const data = '\x1b[1;2;3;4;5;6;7;8;9;0m\x1b[1;2;3;4;5;6;7;8;9;0m\x1b[1;2;3;4;5;6;7;8;9;0m';
212221
let content = '';
213-
while (content.length < 50000000) {
222+
while (content.length < SIZE) {
214223
content += data;
215224
}
216225
parsed = toUtf32(content);
@@ -221,11 +230,11 @@ perfContext('Parser throughput - 50MB data', () => {
221230
}, {fork: true}).showAverageThroughput();
222231
});
223232

224-
perfContext('OSC (short) - OSC 0;hi ST', () => {
233+
perfContext('OSC string interface (short seq) - OSC 0;hi ST', () => {
225234
before(() => {
226235
const data = '\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\x1b]0;hi\x1b\\';
227236
let content = '';
228-
while (content.length < 50000000) {
237+
while (content.length < SIZE) {
229238
content += data;
230239
}
231240
parsed = toUtf32(content);
@@ -236,11 +245,26 @@ perfContext('Parser throughput - 50MB data', () => {
236245
}, {fork: true}).showAverageThroughput();
237246
});
238247

239-
perfContext('OSC (long) - OSC 0;<text> ST', () => {
248+
perfContext('OSC string interface (long seq) - OSC 0;<text> ST', () => {
240249
before(() => {
241250
const data = '\x1b]0;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\';
242251
let content = '';
243-
while (content.length < 50000000) {
252+
while (content.length < SIZE) {
253+
content += data;
254+
}
255+
parsed = toUtf32(content);
256+
});
257+
new ThroughputRuntimeCase('', () => {
258+
parser.parse(parsed, parsed.length);
259+
return {payloadSize: parsed.length};
260+
}, {fork: true}).showAverageThroughput();
261+
});
262+
263+
perfContext('OSC class interface (short seq) - OSC 0;hi ST', () => {
264+
before(() => {
265+
const data = '\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\x1b]1;hi\x1b\\';
266+
let content = '';
267+
while (content.length < SIZE) {
244268
content += data;
245269
}
246270
parsed = toUtf32(content);
@@ -251,11 +275,56 @@ perfContext('Parser throughput - 50MB data', () => {
251275
}, {fork: true}).showAverageThroughput();
252276
});
253277

254-
perfContext('DCS (short)', () => {
278+
perfContext('OSC class interface (long seq) - OSC 0;<text> ST', () => {
279+
before(() => {
280+
const data = '\x1b]1;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\';
281+
let content = '';
282+
while (content.length < SIZE) {
283+
content += data;
284+
}
285+
parsed = toUtf32(content);
286+
});
287+
new ThroughputRuntimeCase('', () => {
288+
parser.parse(parsed, parsed.length);
289+
return {payloadSize: parsed.length};
290+
}, {fork: true}).showAverageThroughput();
291+
});
292+
293+
perfContext('DCS string interface (short seq)', () => {
294+
before(() => {
295+
const data = '\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\';
296+
let content = '';
297+
while (content.length < SIZE) {
298+
content += data;
299+
}
300+
parsed = toUtf32(content);
301+
});
302+
new ThroughputRuntimeCase('', async () => {
303+
parser.parse(parsed, parsed.length);
304+
return {payloadSize: parsed.length};
305+
}, {fork: true}).showAverageThroughput();
306+
});
307+
308+
perfContext('DCS string interface (long seq)', () => {
309+
before(() => {
310+
const data = '\x1bPpLorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\';
311+
let content = '';
312+
while (content.length < SIZE) {
313+
content += data;
314+
}
315+
parsed = toUtf32(content);
316+
});
317+
new ThroughputRuntimeCase('', async () => {
318+
parser.parse(parsed, parsed.length);
319+
return {payloadSize: parsed.length};
320+
}, {fork: true}).showAverageThroughput();
321+
});
322+
323+
perfContext('DCS class interface (short seq)', () => {
255324
before(() => {
256-
const data = '\x1bPq~~\x1b\\';
325+
const data = '\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\';
257326
let content = '';
258-
while (content.length < 50000000) {
327+
while (content.length < SIZE) {
259328
content += data;
260329
}
261330
parsed = toUtf32(content);
@@ -266,11 +335,11 @@ perfContext('Parser throughput - 50MB data', () => {
266335
}, {fork: true}).showAverageThroughput();
267336
});
268337

269-
perfContext('DCS (long)', () => {
338+
perfContext('DCS class interface (long seq)', () => {
270339
before(() => {
271-
const data = '\x1bPq#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0#1~~@@vv@@~~@@~~$#2??}}GG}}??}}??-#1!14@\x1b\\';
340+
const data = '\x1bPqLorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\';
272341
let content = '';
273-
while (content.length < 50000000) {
342+
while (content.length < SIZE) {
274343
content += data;
275344
}
276345
parsed = toUtf32(content);

0 commit comments

Comments
 (0)