Skip to content
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

Performance of primordials #70

Open
debadree25 opened this issue Apr 5, 2023 · 11 comments
Open

Performance of primordials #70

debadree25 opened this issue Apr 5, 2023 · 11 comments

Comments

@debadree25
Copy link
Member

When going through https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md the doc says the use of primordials can cause performance regressions with V8 , is there any explaination/investigation on why this happens and is this something that can be fixed/optimised since primordials are used almost everywhere could this lead to improvements overall? just opening this for discussion and my understanding!

@ronag
Copy link
Member

ronag commented Apr 5, 2023

This has been thoroughly discussed multiple times. I think you can find more info if you search the node issues.

@debadree25
Copy link
Member Author

Hey I went through the issues, it seems most are relevant to mitigating effects of primordials although i was able to find this issue nodejs/node#29766 and the associated upstreams issues https://bugs.chromium.org/p/v8/issues/detail?id=6831 , https://bugs.chromium.org/p/v8/issues/detail?id=9702 both of which seem solved so are there no present perf issues with primordials? or is there any specific discussion you could point to 😅😅😅

@aduh95
Copy link

aduh95 commented Apr 5, 2023

Did you have a look at https://github.com/nodejs/node/blob/5572f8fca0cfc3a2120817ad1b9f18edff3af261/doc/contributing/primordials.md#L100-L124? There are some specific primordials that have been flagged at being bad for perf (the list was manually curated by modifying the streams source primordial by primordial, it's likely not exhaustive and might be outdated). The gist of it seems to be:

  • array method that modify the array itself (array.push / array.unshift / array.pop / array.shift).
  • Function.prototype methods.

My understanding of this issue is that V8 fails to recognize the primordial version and the generated byte code does not use a fast-path.
There is another aspect of primordials that is to try to replace methods of the ES spec that are relying on user-mutable globals (RegExp.prototype.test, Promise.all, String.prototype.split, String.prototype.replace, etc.), but the alternative is almost always slower even though in an ideal world removing the reliance on globals should rather improve perf rather than degrade it. It would be very nice to have a performant solution for those problems, but I haven't found any myself (albeit I didn't look for very long).

@anonrig
Copy link
Member

anonrig commented Apr 5, 2023

@aduh95 I remember @marco-ippolito worked on replacing ArrayPrototypeJoin with our custom implementation for performance, but it got rejected. (nodejs/node#45705)

cc @tniessen for visibility

@aduh95
Copy link

aduh95 commented Apr 5, 2023

nodejs/node#45705 is not really the same discussion, I think when we talk about "the performance of primordials", we mean "the relative perf difference between using a built-in method in the classic way (e.g. array.push(value)) vs using the equivalent primordial (e.g. ArrayPrototypePush(array, value))". In the case of %Array.prototype.join%, my understanding is that both the primordials and the "classic" uses of this API perform the same, just it could be faster, but it's hardly related to primordials.

@tniessen
Copy link
Member

tniessen commented Apr 5, 2023

To answer the original question about the performance of primordials: that is mostly out of our hands. The single most important factor that determines Node.js performance is V8. We can try to optimize our own APIs and internals, but the underlying JS engine's performance as well as hardware specs, I/O performance, etc. remain the dominating factors for most applications.

As was said in the issue that @anonrig refers to, replacing built-in functions with custom implementations is not the goal of primordials, in fact, it goes against the goals of primordials as @aduh95 explained in nodejs/node#45705 (comment). Of course, as mentioned in that comment, we are free to implement faster versions of built-in functions and to use them within Node.js core. The question is: is it worth the effort? Custom implementations add overhead to maintenance, they can reduce readability (one of the pain points with primordials, too), and we would have to benchmark them in every V8 release to ensure they are still faster than built-in functions that achieve the same results. And even if they are faster, it only matters if they are used in hot paths, and there was no evidence for that in nodejs/node#45705.

In Colin's words:

Performance isn't everything.

@debadree25
Copy link
Member Author

Ok so far from the convo I understand that:

  • Some primordials are slow rn, and most of them are at parity it seems (maybe we could make an updated list of this)
  • The primary reason they are slow is how v8 recognizes fast paths of these functions IIUC (I don't know if this is the right place to ask, out of curiosity if we were to try and investigate this for functions that may be slow any pointers on how one could go about that)

Leaving this open for some time in case anyone else would like to chime in but seems like nothing much actionable from nodejs side

@anonrig
Copy link
Member

anonrig commented Apr 5, 2023

but seems like nothing much actionable from nodejs side

I think validating the correctness of the primordials performance section would be an actionable item, since it might be outdated.

@aduh95
Copy link

aduh95 commented Apr 5, 2023

Another potentially actionable item would be to write a benchmark that would let us keep the list up-to-date without going the very manual route of refactoring Node.js internals and run benchmark to see if perf is impacted (but maybe there's no way or writing such benchmark and the manual testing is the only way, I don't know).

  • (I don't know if this is the right place to ask, out of curiosity if we were to try and investigate this for functions that may be slow any pointers on how one could go about that)

I don't remember which is it, but there's a V8 flag that outputs the optimization it's able to do, and sometimes that was able to show using a primordials have an effect on V8 optimizations.

@debadree25
Copy link
Member Author

I think could work on validating the present list, automating the benchmark all ideas seem manual since such huge number of functions to check with!

@ljharb
Copy link
Member

ljharb commented Apr 6, 2023

If there’s something that’s as correct as primordials but faster, that’s what we should use, but doing the wrong thing really fast isn’t an improvement - so I’m not sure what the value would be in using a more fragile approach even if it’s faster.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants