-
Notifications
You must be signed in to change notification settings - Fork 130
Performance improvements #483
Comments
About profiling There is a simple built-in Node.js profiler (https://nodejs.org/en/docs/guides/simple-profiling/). Let's say we want to profile how carto processes osm-carto. We then run Then I got the following:
(Note: The last time I did this which I didn't document I remember getting some 12% in Filterset.addable or the like) |
I'm baffled. Befuddled. Bewildered. Let's start with a recent profile: [Summary]:
ticks total nonlib name
11097 60.1% 65.2% C++
5911 32.0% 34.7% JavaScript
1453 7.9% Shared libraries
163 0.9% 1.0% GC
7 0.0% Unaccounted
[C++ entry points]:
ticks cpp total name
3493 28.5% 18.9% v8::internal::Runtime_StringReplaceGlobalRegExpWithString(int, v8::internal::Object**, v8::internal::Isolate*)
2054 16.7% 11.1% v8::internal::Runtime_Apply(int, v8::internal::Object**, v8::internal::Isolate*)
1900 15.5% 10.3% v8::internal::Runtime_KeyedGetProperty(int, v8::internal::Object**, v8::internal::Isolate*)
892 7.3% 4.8% v8::internal::Runtime_BoundFunctionGetBindings(int, v8::internal::Object**, v8::internal::Isolate*)
[JavaScript]:
ticks total nonlib name
484 2.6% 2.8% LazyCompile: *replace native string.js:146:23
399 2.2% 2.3% Stub: CEntryStub
295 1.6% 1.7% Stub: StringAddStub_CheckNone_NotTenured
269 1.5% 1.6% LazyCompile: ~InnerArrayFilter native array.js:891:26
209 1.1% 1.2% LazyCompile: ~<anonymous> native v8natives.js:1199:16
205 1.1% 1.2% Builtin: ArgumentsAdaptorTrampoline
163 0.9% 1.0% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
[Shared libraries]:
ticks total nonlib name
1241 6.7% /usr/bin/nodejs
205 1.1% /lib/x86_64-linux-gnu/libc-2.24.so
3 0.0% /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
2 0.0% [vdso]
2 0.0% /lib/x86_64-linux-gnu/libm-2.24.so
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
Callers occupying less than 2.0% are not shown.
ticks parent name
1241 6.7% /usr/bin/nodejs
537 43.3% v8::internal::Runtime_Apply(int, v8::internal::Object**, v8::internal::Isolate*)
534 99.4% LazyCompile: ~<anonymous> native v8natives.js:1199:16
534 100.0% LazyCompile: ~InnerArrayFilter native array.js:891:26
534 100.0% LazyCompile: *filter native array.js:915:21
464 86.9% LazyCompile: *tree.Variable.ev /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/variable.js:16:17
55 10.3% LazyCompile: *tree.Operation.ev /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/operation.js:16:39
15 2.8% LazyCompile: ~tree.Variable.ev /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/variable.js:16:17
355 28.6% v8::internal::Runtime_StringReplaceGlobalRegExpWithString(int, v8::internal::Object**, v8::internal::Isolate*)
355 100.0% LazyCompile: *replace native string.js:146:23
55 15.5% LazyCompile: *tree.Filterset.addable /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:105:44
55 100.0% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
41 74.5% LazyCompile: *addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
12 21.8% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
2 3.6% LazyCompile: ~foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
36 10.1% Handler: 67092480#__default__#line-width {5}
31 86.1% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
22 71.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
9 29.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
5 13.9% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
3 60.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
2 40.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
36 10.1% Handler: 67043328#__default__#line-color
35 97.2% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
24 68.6% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
11 31.4% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
1 2.8% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
1 100.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
32 9.0% Handler: 67107840#__default__#line-color
28 87.5% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
18 64.3% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
10 35.7% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
4 12.5% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
2 50.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
2 50.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
30 8.5% Handler: 67107840#__default__#line-width
25 83.3% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
15 60.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
10 40.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
5 16.7% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
5 100.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
29 8.2% Handler: 67107840#__default__#line-color {1}
25 86.2% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
19 76.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
6 24.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
4 13.8% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
3 75.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
1 25.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
28 7.9% Handler: 67107840#__default__#line-cap
25 89.3% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
17 68.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
8 32.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
3 10.7% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
2 66.7% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
1 33.3% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
24 6.8% Handler: 67076096#access#line-dasharray
20 83.3% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
10 50.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
10 50.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
4 16.7% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
4 100.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
23 6.5% Handler: 67106816#__default__#line-width
19 82.6% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
10 52.6% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
9 47.4% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
4 17.4% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
3 75.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
1 25.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
22 6.2% Handler: 67092480#__default__#line-color
17 77.3% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
12 70.6% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
5 29.4% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
5 22.7% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
3 60.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
2 40.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
21 5.9% Handler: 67076096#access#line-color {1}
18 85.7% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
9 50.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
9 50.0% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
3 14.3% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
2 66.7% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
1 33.3% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
12 3.4% LazyCompile: *tree.Quoted.toString /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/quoted.js:10:23
7 58.3% LazyCompile: *DefaultString native runtime.js:657:23
7 100.0% LazyCompile: *ConvertToString native array.js:159:25
2 16.7% LazyCompile: ~<anonymous> /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/expression.js:23:48
2 100.0% LazyCompile: *InnerArrayMap native array.js:1001:23
2 16.7% LazyCompile: *DefaultNumber native runtime.js:642:23
2 100.0% LazyCompile: *ToPrimitive native runtime.js:528:21
1 8.3% LazyCompile: ~tree.Filter.toObject /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filter.js:32:42
1 100.0% LazyCompile: ~tree.Filterset.toObject /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:8:45
109 8.8% v8::internal::Runtime_KeyedGetProperty(int, v8::internal::Object**, v8::internal::Isolate*)
70 64.2% Handler: 67076096#access#line-color
64 91.4% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
44 68.8% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
44 100.0% LazyCompile: *inheritDefinitions /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:537:28
20 31.3% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
20 100.0% LazyCompile: render /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:160:50
6 8.6% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
6 100.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
6 100.0% LazyCompile: *inheritDefinitions /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:537:28
23 21.1% Handler: 67076096#access#line-join
21 91.3% LazyCompile: ~tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
13 61.9% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
13 100.0% LazyCompile: *inheritDefinitions /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:537:28
8 38.1% LazyCompile: *foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
8 100.0% LazyCompile: render /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:160:50
2 8.7% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
2 100.0% LazyCompile: ~addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
2 100.0% LazyCompile: *inheritDefinitions /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:537:28
11 10.1% LazyCompile: *tree.Filterset.addable /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:105:44
11 100.0% LazyCompile: *tree.Filterset.cloneWith /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/filterset.js:59:46
9 81.8% LazyCompile: *addRules /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:473:18
6 66.7% LazyCompile: *inheritDefinitions /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:537:28
3 33.3% LazyCompile: ~inheritDefinitions /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:537:28
1 9.1% LazyCompile: ~foldStyle /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:611:19
1 100.0% LazyCompile: render /var/lib/data/mdione/src/system/osm/carto/lib/carto/renderer.js:160:50
1 9.1% LazyCompile: *tree.Ruleset.flatten /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/ruleset.js:94:22
1 100.0% LazyCompile: *tree.Ruleset.flatten /var/lib/data/mdione/src/system/osm/carto/lib/carto/tree/ruleset.js:94:22 So the plan was to attack the calls to But there seem to be no such call in that function; the closest I could find is the regexp in line 110, so I thought: maybe that regexp is taking too long. I replaced the code like this: /*
if (value.match(/^[+-]?[0-9]+(\.[0-9]*)?([e|E][+-]?[0-9]+)?$/)) {
value = parseFloat(value);
}
*/
f = parseFloat(value);
if (f !== NaN) {
value = f;
} I understand that the code is not equivalent; in particular, if value is like Guess what: instead of ~18s in my system, now it takes ~4m30s! I tried to find other, non-sampling profilers to fully profile the script and I can't find any. The closest I got was https://github.com/ralphv/zoran, but it's a 5 commit tool wrote in two days, one in 2015 and another in 2016, and I couldn't make it work with a CLI tool like As you can see, I'm not a JS developer, so I'm not familiar with its toolchain. I you could provide some pointers on how to actually tackle this, I'll be happy to follow them. |
I also wanted to get rid of the regex already but that resulted in the weird behaviour you noticed which especially breaks OSM Bright. I think the problem here is not the regex itself but that the system spends so much time in addable that it is the most important calculation. The filter system of carto is quite complex so I don't understand it fully yet. There is some experimental branch which tries to modify the filter system and also removes the particular function, see #206. So far I didn't have the need for performance optimizations in JS, so I'm also quite new to the topic. At this point I haven't any specific advice but maybe @tmcw could share some thoughts how he would approach this. |
Well, one thing I learned is that regexps literals are automatically compiled by I found new ways to profile with |
More info:
|
So The latter at least tells us that we're dying of a gazillion of paper cuts: Zoom to one of the more expensive Zoom to a couple of So the conclusion so far is: if we think we can gain some processing time, it will have to be by rethinking those 4 functions. |
Thanks for the time you invested so far! About functions: it was my feeling too that it boils down to those. So we have found that out by research too. The question remains what needs to be done to gain something. |
I have no knowledge about carto but js optimisation. You loose lot of time in the matching.
to
@StyXman suggested something like that but forgot that you can not compare NaN with NaN in JS!
Thats why StyXman code dropped the condition and worked with the parseFloat´ed value in any case. No real wonder that carto exploded as it had to handle NaN instead of a string or number. |
@HolgerJeromin How would you prevent something like that:
Ref #471 (shows a real world case for that problem) |
Backed with jsPerf i would do similar code as jQuery has (explanation):
I hope this does not kill performance again :-) |
When I used an unguarded |
@StyXman Did you read this comment?
|
I did it like this
Unfortunately, I didn't see any gains. The new code is a little bit slower (~100ms). :) But nice suggestion for the parseFloat workaround, thanks. |
Are there any work in progress with this issue? |
I'd suspect the biggest gains lie in big-o/algorithmic optimization instead of JS microbenchmarking and the like. One thing I'd really think about is whether, instead of treating parsing as "generating an AST and then mutating it until it's the stylesheet", it could be treated as a full transform: the unprocessed input and processed output would be entirely different data representations and there could be an intermediate form that uses JavaScript's blessed new datastructures (sets and maps, etc) to optimize lookup and modification. |
Sorry that I dissapeared, my family just expanded. @HolgerJeromin's change doesn't seem to make a change, I still get ~17s for the same version of osm-carto. |
I agree - there don't seem to be obvious big gains in the inner loops. #206 was mentioned above, and that would eliminate the hot function completely, and improve the resulting XML. |
This issue is meant to collect discussions about possible performance optimizations. More detailed sub tasks should be opened once identified.
The text was updated successfully, but these errors were encountered: