-
Notifications
You must be signed in to change notification settings - Fork 15k
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
gRPC roundtrips on Windows are significantly slower in renderer processes than in the main process #14011
Comments
👋 Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can. To help make it easier for us to investigate your issue, please follow the contributing guidelines. |
Can confirm on both
|
Note that grpc uses the libuv bindings fairly extensively. I wonder if it has any impact as well. That's an interesting problem. |
Still an issue on Electron versions
Does anyone have any thoughts on where to begin looking at this? I'm happy to lend a hand, but don't know where to start. |
gRPC author here - I'm not surprised by this at all, given the way our module works. That would cause a lot of useless chatter in the IPC channel between the renderer and the main process. How does this compare with other modules however? I get the feeling the renderer really isn't made for performance-hungry tasks, and that is the reason why you can always delegate back to the main process. |
I don't quite follow this
|
When using the @grpc/grpc-js module, the main & renderer roundtrip times are pretty much equal. Does anything come to mind about why it's so different between the native module and the pure JS one? |
No, as far as I understand with Electron, native modules still run on the main process. grpc is a native module. grpc-js isn't. It is my understanding that if you try to interact with a native module from the renderer process, electron will silently send IPCs back-and-forth the two processes. Maybe I am wrong, but if this is correct, this would explain the workload increase. Maybe there is also something a bit more heavyweight with the Windows IPC implementation which would make it cost more than the Linux one. |
I am another gRPC author. One thing about the grpc native module is that it interacts with the libuv event loop directly, and possibly in an unusual way. The libuv event loop is implemented differently on different platforms and that may also be true of the Chrome event loop. So, that might have an impact on these timings, but I don't know what exactly might be happening there. |
How Electron integrates Chromiums and Node's event loop varies between renderer and main as well, if the module is doing libuv trickery I'm not suprised it's getting mixed results when we are also doing some libuv trickery 😄 |
OK, after some out-of-band discussion we have a new hypothesis for what's happening here. First, an explanation of how the Electron main and renderer "processes" actually work: from an operating system perspective, Electron is actually just one process that does both main and rendering. The main process corresponds to the Node event loop, and the renderer process corresponds to the Chrome event loop. Electron essentially glues the two event loops together so that they run alternately. I am not sure about this part, but I believe that it uses V8's Isolate mechanism to provide the two independent JavaScript environments. I also believe that because native modules can only run on the Node event loop, any invocation of a native module function results in an inter-Isolate message. So, our hypothesis is this: V8's inter-isolate message passing system has strong sandboxing features, and this sandboxing is implemented in a way that is much more heavyweight on 64-bit Windows than on other platforms because of some specifics of the Windows API. Those APIs specifics are different on 32-bit Windows, so one experiment to test this could be running the same benchmark on 32-bit Windows, or at least using the 32-bit versions of electron and grpc (installed with |
This is not the case, as you'll be able to confirm by looking in the task manager.
This is not necessarily the case either. If you
What specifics would those be? |
OK, upon further investigation, it looks like I was wrong about the process thing. But I think there is still some possibility that the same general mechanism is at fault here. Regarding the Windows API, I have been told that this functionality was freely usable under 32-bit Windows but changed to require administrator privileges under 64-bit Windows. I have also been told that V8 used this functionality to implement their sandboxing functionality under 32-bit Windows, and when it changed for 64-bit, instead of using it they re-implemented the same protections in userland. That re-implementation is heavy weight in such a way that cross-sandbox communication would be much more expensive on 64-bit Windows than any other platform. |
I don't think this is accurate; this API looks like a process-wide sandbox, but a) multiple V8 instances can coexist in the same process, so such a sandboxing mechanism wouldn't isolate those instances from each other, and b) process sandboxing is disabled in Electron by default. It's possible that the APIs you mention were at some point used in Chromium's process sandboxing, but that isn't what's at play here. |
Thank you for taking the time to report this issue and helping to make Electron better. The version of Electron you reported this on has been superseded by newer releases. If you're still experiencing this issue in Electron 6.x.y or later, please add a comment specifying the version you're testing with and any other new information that a maintainer trying to reproduce the issue should know. I'm setting the Thanks in advance! Your help is appreciated. |
The issue is still reproducible on the latest Electron versions:
|
The Electron version reported on this issue is no longer supported. See our supported versions documentation. If this is still reproducible on a supported version, please open a new issue with any other new information that a maintainer should know. Thank you for taking the time to report this issue and helping to make Electron better! Your help is appreciated. |
The issue is still reproducible on the latest Electron versions:
However, this issue doesn't really matter anymore: |
Expected Behavior
I am using the
grpc
native node module in an Electron app. I expect similar roundtrip performance when sending gRPC messages in the main process vs. in the renderer process.Actual behavior
On macOS, performance is equivalent. On Windows, the roundtrip times for gRPC messages sent from the renderer process are, on average, ~200x slower than when sent from the main process.
To Reproduce
This repo shows the described behavior: https://github.com/descriptinc/grpc-electron-test/tree/sr/electron-2.0
It runs the same gRPC test in the main process and in the renderer process and then shows the results in the renderer BrowserWindow. It computes the average round-trip time for a simple gRPC message (200 messages, sent at an interval of 10 ms. The problem persists even if the interval is much greater).
On Windows I get a result like:
While on macOS I get a result like:
Screenshots
Windows:
macOS:
I get the expected behavior in Windows if I switch to the alpha/experimental
@grpc/grpc-js
module. See: https://github.com/descriptinc/grpc-electron-test/tree/sr/electron-3.0-grpc-js (Electron 3 is required because@grpc/grpc-js
requires node >= 9.0.0).Additional Information
I'm not sure if the real issue here is in Electron or in gRPC. I opened an issue there, too: grpc/grpc-node#489
I'm guessing that this is somehow related to the different way that Windows and Mac/Linux implement the node.js runtime binding in the renderer process, i.e.,
The text was updated successfully, but these errors were encountered: