Closed Archmonger closed 1 year ago
@rmorshea Is there any way to allow me to modify required tests for this repo?
I just bumped Python versions to 3.9+ as a result of core's requirements and a general need for py3.9+ asyncio.
@rmorshea Do you want to review or should I merge?
I am going to merge. If you provide a post-merge review I will PR any needed review contents.
Description
This PR exists to test out what performance gains can be accomplished when running ReactPy in a backhaul thread.
settings.py:REACTPY_BACKHAUL_THREAD
Technical Background
Our existing rendering process does not have the capability to render components while the webserver is doing other things. Below is an oversimplified graphic demonstrating the order of operations we typically go through.
With the implementation above, ReactPy can queue up numerous
render
andsend
events that can flood the main Python thread. This issue is compounded by the fact that our component functions are synchronous, which breaks the operational flow of event queues. This PR moves rendering tasks outside of the main Python thread, allowing for the webserver to render ReactPy components in parallel to anything else. Below is an oversimplified graphic demonstrating this parallelism.The graphic above covers the high level concept, however, some additional complexity that isn't depicted. The biggest missed is the preemptive multitasking between the ASGI loop and the backhaul loop. This allows the Python to properly utilize CPU downtime during
send
events to process other things that hit the webserver.The level of performance gained from this will depend on the the loop implementation. For example,
uvloop
can gain great efficiency from this.Test Configuration
asyncio
uvloop
Results
TLDR: On Linux, threading makes everything faster. On Windows, everything is faster except for renders per second under load.
uvloop
andbackhaul threads
on Linux, our ~12000 RPS is competitive with roughly equivalent tests on HTTP ASGI frameworks.renders per second
duringlow rendering
workloads issignificantly faster
renders per second
duringheavy rendering
workloads isslightly faster
on Linux butslightly slower
on Windowstime to load
duringheavy rendering
workloads isextremely faster
time to load
duringheavy network IO
workloads issignificantly faster
time to load
duringheavy mixed rendering and network IO
workloads isextremely faster
event driven renders
duringheavy rendering
workloads issignificantly faster
Time To Load
These tests check how long it takes 250 components to be rendered on the page. One test case needed to be limited to 50 components due to browser limitations.
Simple: These are effectively "Hello World" components with no logic. They only contain body text. Counter: These components simulate how quickly new components would load when the main ASGI event loop is busy re-rendering other components. Time to load does not include the initial HTTP render. Net IO: These components render approximately 10MB worth of VDOM data only once. Mixed: These components continuously re-render approximately 1MB worth of VDOM data. Limited to 50 components and 1MB payload size to prevent the browser tab memory limit. All scenarios below suffered from WS connection time out instability during long runs, likely because 1MB single-packet payloads is unrealistically large.
uvicorn
daphne
hypercorn
uvicorn
+ backhauldaphne
+ backhaulhypercorn
+ backhaulRenders Per Second
_These tests use components that continuously re-render themselves by incrementing a number via
use_effect
. Tests were re-run with various quantities of components._uvicorn
daphne
hypercorn
uvicorn
+ backhauldaphne
+ backhaulhypercorn
+ backhaulEvent Triggered Renders
These tests check the timing of
click event
→new thing displayed
while the webserver is exposed to various different conditions.RPS 250: Event triggered renders per second. This is the latency a user should feel if clicking on the page while the webserver is busy with a heavy rendering workload. RPS 250 Avg RT: Average round trip timing which demonstrates event latency during heavy workloads. A "Round Trip" involves the following:
client click event
→server WS receive
→server event handler
→server set state
→server layout render
→server data transmit to client
→client layout shift
. Event Avg RT: Average event timing it takes for simple components to perform a round-trip render during light workloads. Render is triggered by a client click event. This shows our minimum event latency.uvicorn
daphne
hypercorn
uvicorn
+ backhauldaphne
+ backhaulhypercorn
+ backhaulUvicorn Weird Behaviors
uvicorn
appears to create an exceptions when rendering above ~7500 RPS on Windows. Either a bug within theuvicorn
package or theWindowsProactorEventLoop
implementation. This exception does not appear to impact anything. See exception below:Ctrl + C
from closing out the webserver. This is a Windows-only bug likely related to howuvicorn
does Windows subprocesses (does not forwardSIGINT
to threads).Daphne Weird Behaviors
daphne
performance is sometimes fast, it has a lot of performance jitter. During high workloads, ASGI requests get processed in chunks rather than incrementally.daphne
's performance always starts off strong but continuously slows down over time. In comparison,hypercorn
anduvicorn
have far more consistent performance.daphne
had frequent WS disconnects (during heavy workloads) and event queue stalling (during light workloads).