-
Notifications
You must be signed in to change notification settings - Fork 203
Possibility to Improve Performance with Precompiled Templates/Classes ? #205
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
Comments
Have you looked at registering React as an extension (3rd argument to My understanding is that extensions registered this way will last as long as the process does. So if Nginx works like apache, that will mean as long as the worker process is serving requests. Not quite the same as an op-code cache available to all processes, but worth a shot. |
@kynx I have not looked at I'm not sure that hooking into Nginx/Apache would provide any benefit as the dynamic data comes from PHP; unless PHP could somehow utilize a loaded version of React (#1 above) from the FPM processes. I would think that serialization would provide the greatest immediate benefit with the least amount of modification to the stack as developers could then cache the React Library (#1) above. I was also wondering if there would be a way to even precompile JSX templates, without data (props/state) such that performing a render-serverside (or even client side) with data could be even faster? |
I've confirmed that using Since there is 0 performance gain with @stesie Do you know if this is possible? It would open the door for a larger trend in server rendering. |
I think this all is a mixture of different questions, so let me pick and answer one after another ... V8 Engine Startup TimingRegarding the mod_php/fpm SAPI handling: opposed to the CLI & CGI SAPIs, those SAPIs handle multiple requests, however just one request after another (in non-ZTS setups parallel requests are handled by multiple processes). Nevertheless V8 itself is currently initialized upon first V8Js object construction, subsequent object constructions (possibly from different requests) then can take advantage of the already initialized V8 engine. This is, there already should be an (timewise) enormous difference if you do the rendering twice within the same process, since the first try starts up V8 itself + compiles/executes the script. The second try solely compiles/executes the script. So far you've just provided numbers for the first case, not the latter. You might want to give that a try to give a clue of potential time saving A possible action would be that we change the extension to (optionally) spin up the V8 engine when the PHP extension is loaded. This way V8 would already be started when Apache/FPM spin up spare server processes (i.e. before handling requests) Extensionsto make it short: forget about them :) Personally I think they do more harm then good, after all you just provide JavaScript code upfront which is shared amongst requests (as they are handled internally by V8 then) ... and they're simply re-evaluated upon instanciation, almost like if just just Unbound scriptsV8 supports so-called unbound scripts (http://v8.paulfryzel.com/docs/master/classv8_1_1_unbound_script.html) which are compiled but neither bound to a context nor executed -- that might be an option to gain performance. However PHP-V8Js currently uses To get a feeling of how much could be gained that way, just run all your sources through Heap snapshotsV8 supports snapshots (and up to version 4.4 they were working with shared libraries) ... and if you're compiling with snapshots it automatically generates a snapshot and links it right into the V8 library. But be aware that every V8Js context then has the code baked into it and you cannot have an instance without it (even over multiple requests on a single process). The V8 C++ API allows to do snapshotting & loading on run time so you don't have to actually compile it into V8 library itself, but do it from PHP side ... (however the sharing over multiple requests still applies) Of course the crashing-problem of recent V8 versions built with snapshots enabled has to be solved then. Sticking with version 4.4 is not a wise option as it gets no security support anymore. |
@stesie Thanks for the awesome feedback. I've somewhat came to the same conclusions as you. My thoughts in exploring the V8JS world with PHP: RegisterExtension Repeat Renders
So while satisfied with the overall render performance, my only concern was about the worst actor -- the library instantiation for the relatively unchanging library + components. Sure they may change on subsequent builds; however, any cache clearing, rebuilding, precompiling phases would handle that. Snapshots Do you have any information on how to do this or references to documentation?
Unbound Scripts This same concept goes for moving the V8JS Regardless, if it were somehow possible to cache the V8JS isolates with rendered library code via some identifier, that would be huge. If it can't be done because of the internals of V8Js would it be possible to somehow serialize the isolate/contexts such that application caching could be implemented? |
Repeat RendersI'm not sure whether we talk past each other regarding timing. In pseudo code:
... as I understand your last comment I actually would be interested in The latter could be compensated by changing the extension to immediately initialize V8 (instead of doing so lazily) snapshotsI haven't tried building V8 as a shared library with snapshots enabled lately, it was back when 4.5.x or maybe 4.6.x were bleeding edge. Actually I never found the time to really investigate what the problem was, nor did I report to V8 devs. Also see https://bugs.chromium.org/p/v8/issues/detail?id=4192 Regarding old versions you might consider it if you really know what code you're running -- or stated the other way round: never use old versions if you rely on V8Js as a sandboxing tool to run untrusted/customer code as there are no updates (security fixes) for V8 older then 4.8 (currenty stable version). Snapshotting generally only influences startup speed (as you noticed 110ms vs. 60ms), but shouldn't effect runtime performance thereafter. I haven't tried out so far, but a few pointers
to apply them later on
I don't know which is the better option, probably the former one as it seems to allow to choose the snapshot on an isolate by isolate base. serialization generallyI don't know of any way to serialize, cache, clone or whatever an isolate. |
Hmmm, having given it another try it indeed is V8Js' fault... If I modify
before initializing the platform as well as the library, everything works fine (with V8 version 5.0) 😺 snapshot quick startcreate file doublify.js function doublify(x) {
return 2 * x;
} ... and create a snapshot with it embedded:
Then modify said v8::V8::InitializeExternalStartupData(
"/home/stesie/Projekte/v8/out/native/natives_blob.bin",
"/home/stesie/Projekte/v8/out/native/custom_snapshot.bin"
); make ...
... and there it is :-) |
Here is timing information:
// $appScript = React.js + ReactServer.js + App Components + window/console globals
$t = microtime(true);
$v8js = new V8Js();
echo ('Construct: ' . number_format((microtime(true) - $t) * 1000, 4) . ' ms') . PHP_EOL;
$t = microtime(true);
$v8js->executeString($appScript);
echo ('Execute Library Code: ' . number_format((microtime(true) - $t) * 1000, 4) . ' ms') . PHP_EOL;
$t = microtime(true);
$v8js->executeString('ReactDOMServer.renderToString(React.createElement(Table,' . $jsonEncodedProps . '))');
echo ('Execute Component #1: ' . number_format((microtime(true) - $t) * 1000, 4) . ' ms') . PHP_EOL;
$t = microtime(true);
$v8js->executeString('ReactDOMServer.renderToString(React.createElement(Table,' . $jsonEncodedProps2 . '))');
echo ('Execute Component #2: ' .number_format ((microtime(true) - $t) * 1000, 4) . ' ms') . PHP_EOL;;
$t = microtime(true);
$v8js->executeString('ReactDOMServer.renderToString(React.createElement(Table,' . $jsonEncodedProps3 . '))');
echo ('Execute Component #3: ' .number_format ((microtime(true) - $t) * 1000, 4) . ' ms') . PHP_EOL;
$t = microtime(true);
$v8js->executeString('ReactDOMServer.renderToString(React.createElement(Table,' . $jsonEncodedProps4 . '))');
echo ('Execute Component #4: ' .number_format ((microtime(true) - $t) * 1000, 4) . ' ms') . PHP_EOL; |
@stesie I'm going to try to to build with snapshot of the react library if possible to see if I can reduce that 60ms by compiling extension manually with your suggestions and include in my above timing. If the snapshot heap reduces significantly, would it be possible to update V8JS |
Sure we can add some stuff to allow for custom snapshots. Before doing so I'd like to further test whether snapshots can be applied on an isolate by isolate base and then provide the API accordingly. I'd even consider to generate snapshots with V8Js, ... so you can create these, cache the result and re-use as needed. Even so it might be wise to support "global" custom snapshots as well so you need not pass the full snapshot on every request ... Already looking forward to the snapshot-based timings ... exciting ... :) |
Sweet. Yes, generating snapshots via V8Js would be even easier (less work for deployment/CI build process). And global snapshots would also be awesome. Pass in the snapshot, and register is full extension wise (as mentioned earlier). Will keep you posted, stay tuned. |
Unable to compare with V8-v4.4.9.1 as |
so I was curious enough now to give it a try on my own ... I've picked https://github.com/reactjs/react-php-v8js/tree/master/example and did some performance measurements, see this chart: I've tried with two different V8 versions with snapshots disabled and the 5.0.104 from yesterday with snapshots enabled. Once just with the "out of the box" snapshot and one with react included (excluding app components; which is just a table component in the example's case) Stuff to learn from that
//var GLOBAL_MOUNT_POINT_MAX = Math.pow(2, 53);
var counter = 4906291055034368; // hard-coded random number FTW
var ServerReactRootIndex = {
createReactRootIndex: function () {
return counter ++;
//return Math.ceil(Math.random() * GLOBAL_MOUNT_POINT_MAX);
}
};
So it looks really promising :-) |
The custom snapshots results look very amazing. Are the timings you provided in milliseconds as well? I'm still trying to get the snapshot pull to compile locally, once I do I'll compare as well against my baseline snapshot test above. |
yeah right, those are milliseconds too. The values themselves are averages over 100 samples each. |
TIMING RESULTS4.4.9.1 - With Snapshot
Total: ~ 65.2 ms 5.0.104 - With Snapshot, V8JS-PR #207
Total: ~ 23.35 ms
Total: ~ 18.03 ms Woot Woot ✔ |
@stesie Thanks for the fabulous work integrating snapshot support and removing the 4.4.x snapshot dependency! Using V8 with V8JS is now highly optimized for large library/applications 👍
|
you're welcome! And thanks to you, @virgofx, as well, for debating, testing and pushing me forward :) Would you like to set the phpv8-stubs stuff up? I'd create a account under phpv8 org then and grant you member rights on that one then. |
|
Yes, I can handle that, feel free to create repo and grant me rights. I'll do everything up to current and once you tag 5.0 , then I'll update stubs accordingly. |
@virgofx just created the repo and granted you the rights (don't know whether Github sends mail notifications, probably yes ...) |
Although this issue is old and closed I just wanted to add my two cents here. With nginx -> spawn FPM/handle request/return response/close the V8Js is re-initialized with each request. But thanks to the ReactPHP and PHP-PM https://github.com/php-pm/php-pm it is now possible to keep the application and therefore V8JS in memory for subsequent requests. This, in my opinion, will eliminate the V8JS initialization altogether. I yet have to try this but I am optimistic about the conclusion. @stesie do you think it would be a better idea to keep the V8JS in memory for a long period of time? I am hoping there are no memory leaks. |
This approach is similar to making the extension persistent within FPM -- which can be done at the extension level without requiring any other |
@aftabnaveed if you use php-v8js on php-fpm it does not reinitialize V8 on each and every request. What indeed is created on every request (and any subsequent call to |
Would it be possible to precompile templates and classes with an additional method(s) such that performance improvements could be made for server side rendering?
A couple of examples...
1) Cache React Library
Loading the initial V8Js() with the executeString of the current React + ReactServer library takes ~60ms on my current vagrant machine [Ubuntu Trusty, Nginx, PHP56, 2GB RAM, V8-4.4, with snapshot), ~110ms with current V8-5.0.81-no-shapshot. If this is used in high production it would be great to either somehow serialize this instance or have capability to clone first and serialize (leave it up to caller to handle caching technique -- files, memory backed data store) for high throughput
2) Cache Template/Class Collection
Would it be possible to precompile classes such that when rendering components with props generated load times would be faster? For small components render times averaged between 1 to 20ms.
Thoughts? Any other areas to potentially improve performance?
The text was updated successfully, but these errors were encountered: