[02:50:22] doing chromium profiling in response to a suggestion by Krinkle, I noticed that less than half the time spent in domEval() is actually compiling and executing JS, the rest seems to be DOM overhead [02:52:01] in one instance on a VE load, 59ms total for domEval (self 13ms), 46ms for appendChild (self 21ms), 25ms for "Evaluate Script" [02:56:37] if I use new Function() instead, the equivalent domEval call is 41ms [02:59:16] compilation is slightly deferred with the function constructor -- instead of compilation up front, it appears with a stack trace of implement/setAndPropagate/requestIdleCallback [03:00:36] I don't know why eval is done via the DOM instead of with the function constructor. I traced it back to jQuery.globalEval() but couldn't find an explanation there either. [03:08:02] never mind I found https://github.com/jquery/jquery/pull/1449 with an explanation of why jQuery does what it does [03:24:26] I don't think the reason is relevant for us [03:45:30] I uploaded https://gerrit.wikimedia.org/r/c/mediawiki/core/+/945005 , I suggest that as a place for further discussion [07:19:37] I also made https://phabricator.wikimedia.org/T343407 for the related function serialization work [08:09:52] TimStarling: if the dynamic code contains a syntax error, using the function constructor causes an exception in the context of mw.loader; with domEval, not. [09:39:13] Global eval didn't used to involve DOM, it was classic eval() with window/global context. [09:39:43] Which we need for site/user script (string instead of function implementation). [09:40:11] And for other modules is beneficial in that it avoids leaking/exposing RL internals into module scope [09:41:08] Store docs, you know already, but for the record, is more for defragmentation than plain caching, which the browser can and does do as well. https://www.mediawiki.org/wiki/ResourceLoader/Architecture#Store [09:41:49] The batching should mask most of that overhead but indeed we never revisited it, in part because the switch to domEval happened behind our backs in jQuery. [09:46:08] I then kept it as is when we merged mw.loader into startup (which requires making it dependency free) https://www.mediawiki.org/wiki/Wikimedia_Performance_Team/Sprints#2018, https://phabricator.wikimedia.org/T192623 [09:47:05] Thus probably saves enough that we can afford to unbatch and give each its own source url comment [09:47:23] Old patch: https://gerrit.wikimedia.org/r/c/mediawiki/core/+/156544 [09:56:45] Regarding 'new Function', that's seems perfect indeed. http://perfectionkills.com/global-eval-what-are-the-options/#new_function [09:57:12] It's not global but not local either, it's a function called from global scope which is good enough for us [09:57:36] Especially since we're wrapping it in a function anyway [10:02:28] TimStarling: the original concern for the benchmarking was whether js engines would compile the load.php response and its many implement calls right away or lazily. The main difference there, apart from being likely more wasteful to compile twice and with many switching costs; is that eager compilation would mean it happens off the main thread as part of the network response more or less. Whereas deferring it means it happens on the main [10:02:28] thread synchronously at call time, thus moving it into visible territory with jank or Long Task warnings accordingly (which we monitor in navtiming). [10:02:51] At least, that's my current understanding. [23:24:17] I did read that perfectionkills article, and also the discussion at https://github.com/jquery/jquery/pull/1449 which linked to it [23:24:42] did you notice that the source for performance there, http://jsperf.com/tag-vs-eval-fn-construction , is a broken link and is not archived by archive.org? [23:25:39] it's too old to be useful anyway, 2013 [23:26:03] the perfectionkills one is from 2010 but nothing has changed in the specs [23:26:18] but performance definitely has changed [23:31:51] > likely more wasteful to compile twice and with many switching costs [23:32:23] Profiling doesn't support that, it shows large files being compiled in tiny fragments taking microseconds to compile each bit [23:33:48] three are some recent blog posts from the v8 devs about this, like https://v8.dev/blog/preparser [23:34:35] parsing is done in advance, and an AST is stored, but compilation to bytecode is deferred as late as possible and is done in tiny fragments [23:38:10] > eager compilation would mean it happens off the main thread as part of the network response more or less [23:39:26] my profiling shows that compilation resulting from domEval() happens in the main thread [23:40:35] there is some background parsing but it seems to be as a result of I am not seeing any background parsing [23:46:05] according to https://v8.dev/blog/background-compilation , background compilation is only used for "top-level script code and IIFEs", any nested functions are lazy-compiled instead [23:57:03] is there any way to extract actually useful timings from fresnel? like maybe running it locally?