Skip to content

Commit 820a1ec

Browse files
jensjohaCommit Queue
authored andcommitted
[analysis server] Don't allocate on each compare when sorting in semanticTokens/full
Upon a `textDocument/semanticTokens/full` request the analysis server sorts the "tokens" via `sort` on List giving it a compare method. The method, though, created a map literal for each comparison. This CL "inlines" the map (it only had one entry anyway) and adds a benchmark, where I note these changes: ``` 1000: Difference at 95.0% confidence -0.0274151 +/- 0.0135978 -33.1981% +/- 16.4662% (Student's t, pooled s = 0.0181832) 2000: Difference at 95.0% confidence -0.027344 +/- 0.0096196 -20.9529% +/- 7.3712% (Student's t, pooled s = 0.0128635) 4000: Difference at 95.0% confidence -0.108847 +/- 0.0196627 -35.3172% +/- 6.37987% (Student's t, pooled s = 0.0262932) 8000: Difference at 95.0% confidence -0.20647 +/- 0.0272577 -34.4932% +/- 4.55371% (Student's t, pooled s = 0.0364494) 16000: Difference at 95.0% confidence -0.384931 +/- 0.0401393 -31.4809% +/- 3.28272% (Student's t, pooled s = 0.0536748) ``` Change-Id: Ibacce056cd3f154afc9377805bbccfd8c5ca9ea9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/456341 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Jens Johansen <[email protected]>
1 parent 06e54c8 commit 820a1ec

File tree

3 files changed

+137
-8
lines changed

3 files changed

+137
-8
lines changed

pkg/analysis_server/lib/src/lsp/semantic_tokens/encoder.dart

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,6 @@ class SemanticTokenInfo {
204204
SemanticTokenInfo t1,
205205
SemanticTokenInfo t2,
206206
) {
207-
var priorities = {
208-
// Ensure boolean comes above keyword.
209-
CustomSemanticTokenTypes.boolean: 1,
210-
};
211-
212207
// First sort by offset.
213208
if (t1.offset != t2.offset) {
214209
return t1.offset.compareTo(t2.offset);
@@ -219,9 +214,9 @@ class SemanticTokenInfo {
219214
return -t1.length.compareTo(t2.length);
220215
}
221216

222-
// Next sort by priority (if different).
223-
var priority1 = priorities[t1.type] ?? 0;
224-
var priority2 = priorities[t2.type] ?? 0;
217+
// Next sort by priority (if different) (boolean comes above keyword).
218+
var priority1 = t1.type == CustomSemanticTokenTypes.boolean ? 1 : 0;
219+
var priority2 = t2.type == CustomSemanticTokenTypes.boolean ? 1 : 0;
225220
if (priority1 != priority2) {
226221
return priority1.compareTo(priority2);
227222
}

pkg/analysis_server/tool/benchmark_tools/lsp_messages.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,16 @@ class LspMessages {
244244
},
245245
};
246246
}
247+
248+
static Map<String, dynamic> semanticTokensFull(Uri uri, int id) {
249+
return {
250+
'jsonrpc': '2.0',
251+
'id': id,
252+
'method': 'textDocument/semanticTokens/full',
253+
'params': {
254+
'textDocument': {'uri': '$uri'},
255+
},
256+
'clientRequestTime': DateTime.now().millisecondsSinceEpoch,
257+
};
258+
}
247259
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import '../language_server_benchmark.dart';
8+
import '../lsp_messages.dart';
9+
import '../run_utils.dart';
10+
11+
/// Request "semanticTokens/full" in a big file.
12+
Future<void> main(List<String> args) async {
13+
await runHelper(
14+
args,
15+
LspRequestSemanticTokenFull.new,
16+
createData,
17+
sizeOptions: [1000, 2000, 4000, 8000, 16000],
18+
extraIterations: (_) => [null],
19+
runAsLsp: true,
20+
);
21+
}
22+
23+
RunDetails createData(
24+
Uri packageDirUri,
25+
Uri outerDirForAdditionalData,
26+
int size,
27+
dynamic unused,
28+
List<String> args, {
29+
// unused
30+
required dynamic extraInformation,
31+
}) {
32+
Uri libDirUri = packageDirUri.resolve('lib/');
33+
Directory.fromUri(libDirUri).createSync();
34+
35+
var mainFileUri = libDirUri.resolve('main.dart');
36+
var mainFileContent = StringBuffer();
37+
for (int i = 0; i < size; i++) {
38+
var className = 'Class$i';
39+
mainFileContent.write('''
40+
class $className { // line 0 (in the first class)
41+
final int foo; // line 1 (in the first class)
42+
$className(this.foo) { // line 2 (in the first class)
43+
print("Hello from class $className"); // line 3 (in the first class)
44+
print("$className.foo = \$foo");
45+
}
46+
}
47+
''');
48+
}
49+
var mainFileContentString = mainFileContent.toString();
50+
File.fromUri(mainFileUri).writeAsStringSync(mainFileContentString);
51+
52+
return RunDetails(
53+
mainFile: FileContentPair(mainFileUri, mainFileContentString),
54+
);
55+
}
56+
57+
class FileContentPair {
58+
final Uri uri;
59+
final String content;
60+
61+
FileContentPair(this.uri, this.content);
62+
}
63+
64+
class LspRequestSemanticTokenFull extends DartLanguageServerBenchmark {
65+
@override
66+
final Uri rootUri;
67+
@override
68+
final Uri cacheFolder;
69+
70+
final RunDetails runDetails;
71+
72+
LspRequestSemanticTokenFull(
73+
super.args,
74+
this.rootUri,
75+
this.cacheFolder,
76+
this.runDetails,
77+
) : super(useLspProtocol: true);
78+
79+
@override
80+
LaunchFrom get launchFrom => LaunchFrom.Dart;
81+
82+
@override
83+
Future<void> afterInitialization() async {
84+
await send(
85+
LspMessages.open(runDetails.mainFile.uri, 1, runDetails.mainFile.content),
86+
);
87+
88+
// Get colors just to make sure everything is analyzed.
89+
await (await send(
90+
LspMessages.documentColor(runDetails.mainFile.uri, largestIdSeen + 1),
91+
))!.completer.future;
92+
93+
for (int i = 0; i < 5; i++) {
94+
// The below is a replay of a recorded actual IDE interaction.
95+
var allStopwatch = Stopwatch()..start();
96+
97+
// Send semanticTokens/full request.
98+
await (await send(
99+
LspMessages.semanticTokensFull(
100+
runDetails.mainFile.uri,
101+
largestIdSeen + 1,
102+
),
103+
))!.completer.future;
104+
105+
var elapsedWholeThing = allStopwatch.elapsed;
106+
107+
if (latestIsAnalyzing) {
108+
await waitWhileAnalyzing();
109+
elapsedWholeThing = allStopwatch.elapsed;
110+
}
111+
durationInfo.add(
112+
DurationInfo('Semantics tokens full request ($i)', elapsedWholeThing),
113+
);
114+
}
115+
}
116+
}
117+
118+
class RunDetails {
119+
final FileContentPair mainFile;
120+
121+
RunDetails({required this.mainFile});
122+
}

0 commit comments

Comments
 (0)