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

gRPC roundtrips on Windows are significantly slower in renderer processes than in the main process #14011

Closed
srubin opened this issue Aug 10, 2018 · 18 comments

Comments

@srubin
Copy link

srubin commented Aug 10, 2018

  • Electron Version: 2.0.7, 3.0.0-beta.4
  • Operating System (Platform and Version): Windows 10. Windows Server 2016.
  • Last known working Electron version: -

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

$ git clone -b sr/electron-2.0 https://github.com/descriptinc/grpc-electron-test
$ cd grpc-electron-test
$ yarn install

# in one terminal
$ node example/greeter_server.js

# in another terminal
$ yarn start

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:

Average gRPC roundtrip (running in main): 0.62ms
Average gRPC roundtrip (running in renderer): 145.94ms

While on macOS I get a result like:

Average roundtrip (running in main): 1.29ms
Average roundtrip (running in renderer): 1.33ms

Screenshots
Windows:
image

macOS:
image

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).

image

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.,

@welcome
Copy link

welcome bot commented Aug 10, 2018

👋 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.

@ckerr
Copy link
Member

ckerr commented Aug 10, 2018

Can confirm on both 2.0.0 and 3.0.0-beta.7

OS Electron Main Time (ms) Renderer Time (ms)
Ubuntu 18.04 2.0.7 1.61 1.65
Ubuntu 18.04 3.0.0-beta.4 1.81 1.92
Mac 10.13.6 2.0.7 1.29 1.32
Mac 10.13.6 3.0.0-beta.4 1.33 1.37
Win 10 2.0.7 1.11 122.46
Win 10 3.0.0-beta.4

@ckerr ckerr added platform/windows bug 🪲 2-0-x status/confirmed A maintainer reproduced the bug or agreed with the feature 3-0-x labels Aug 10, 2018
@ckerr ckerr added this to Triage: Needs Review in 3.0.x / 3.1.x Aug 10, 2018
@nicolasnoble
Copy link

Note that grpc uses the libuv bindings fairly extensively. I wonder if it has any impact as well. That's an interesting problem.

@ckerr ckerr moved this from Triage: Needs Review to Triage: Doesn't Block Stable in 3.0.x / 3.1.x Aug 10, 2018
@pdesantis
Copy link
Contributor

pdesantis commented Feb 7, 2019

Still an issue on Electron versions 4.0.4 and 5.0.0-beta.2

OS Electron Main Time (ms) Renderer Time (ms) Source Code
Win 10 4.0.4 2.4 141.81 electron-4.0
Win 10 5.0.0-beta.2 1.85 142.6 electron-5.0

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.

@nicolasnoble
Copy link

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.

@srubin
Copy link
Author

srubin commented Feb 7, 2019

I don't quite follow this

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.

  • gRPC from Renderer is performant on macOS, but not on Windows.
  • What chatter on the IPC channel are you referring to? Doesn't each Electron Renderer process runs its own node process?

@pdesantis
Copy link
Contributor

How does this compare with other modules?

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?

@nicolasnoble
Copy link

  • What chatter on the IPC channel are you referring to? Doesn't each Electron Renderer process runs its own node process?

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.

@murgatroid99
Copy link

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.

@MarshallOfSound
Copy link
Member

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.

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 😄

@murgatroid99
Copy link

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 npm install electron --arch=ia32 and npm install grpc --target_arch=ia32). If this hypothesis is correct, then that benchmark may show similar results to what you see on other platforms.

@nornagon
Copy link
Member

nornagon commented Feb 8, 2019

from an operating system perspective, Electron is actually just one process that does both main and rendering

This is not the case, as you'll be able to confirm by looking in the task manager.

native modules still run on the main process

This is not necessarily the case either. If you require a native module in the renderer, that module will be loaded in the renderer process.

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

What specifics would those be?

@murgatroid99
Copy link

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.

@nornagon
Copy link
Member

nornagon commented Feb 8, 2019

V8 used this functionality to implement their sandboxing functionality

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.

@electron-triage
Copy link

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 blocked/need-info label for the above reasons. This issue will be closed 7 days from now if there is no response.

Thanks in advance! Your help is appreciated.

@electron-triage electron-triage added the blocked/need-info ❌ Cannot proceed without more information label Feb 19, 2020
@srubin
Copy link
Author

srubin commented Feb 19, 2020

The issue is still reproducible on the latest Electron versions:

OS Electron Main Time (ms) Renderer Time (ms) Source Code
Win 10 8.0.1 2.95 142.67 electron-8.0
Win 10 9.0.0-beta.2 3.75 138.88 electron-9.0 (beta 2)

@electron-triage electron-triage removed the blocked/need-info ❌ Cannot proceed without more information label Feb 19, 2020
@sofianguy sofianguy added this to Unsorted Issues in 9-x-y Feb 19, 2020
@sofianguy sofianguy added this to Unsorted Issues in 8.2.x Feb 19, 2020
@sofianguy sofianguy moved this from Unsorted Issues to Does Not Block Stable in 9-x-y Feb 19, 2020
@electron-triage
Copy link

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.

@srubin
Copy link
Author

srubin commented Mar 9, 2021

The issue is still reproducible on the latest Electron versions:

OS Electron Main Time (ms) Renderer Time (ms) Source Code
Win 10 12 0.3 497.56 electron-12

However, this issue doesn't really matter anymore: grpc has been deprecated in favor of @grpc/grpc-js, which doesn't have this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
3.0.x / 3.1.x
Confirmed Issues
5.0.x
Unsorted Issues
8.2.x
Unsorted Issues
9-x-y
Does Not Block Stable
Development

No branches or pull requests

9 participants