reactpy icon indicating copy to clipboard operation
reactpy copied to clipboard

Improve IDOM Performance

Open Archmonger opened this issue 3 years ago • 5 comments
trafficstars

Current Situation

Currently, IDOM can render roughly 18 simple components per second.

Proposed Changes

Find the hot spots in IDOM's code-base in order to identify where attention should be focused. Once we know this, we will be able to focus our efforts on performance improvements.

Implementation Details

Analysis

Use Yappi to profile IDOM dispatchers and determine what is taking the most significant amounts of time.

We will also need to figure out how to test websocket concurrency. It seems like there are very few tools for this, especially for our applications. We're probably going to have to create out own benchmarking tools. Here's some resources we might want to read through later.

  • https://healeycodes.com/websocket-benchmarker
  • https://kemalcr.com/blog/2016/11/13/benchmarking-and-scaling-websockets-handling-60000-concurrent-connections/
  • https://ma.ttias.be/benchmarking-websocket-server-performance-with-artillery/

Sync and Async

Currently, IDOM relies on synchronous code to properly queue renders. Sync code is known to blocks the asyncio event loop, which causes concurrency issues.

See section "Two Worlds of Python" for why this is a problem: https://arunrocks.com/a-guide-to-asgi-in-django-30-and-its-performance/

All synchronous functions should be converted to async within IDOM core. See idom-team/django-idom#31 for the original discussion on this.

Extreme Countermeasures

If optimizations can't be suitably performed within Python, consider writing C/C++ code exposed via Python APIs that performs the same functionality as the non-performant parts of IDOM.

Timeframe and Goals

If major changes need to occur, then this should be targeted for somewhere around v3 of IDOM.

Since it is a realistic scenario to use IDOM to render a full webpage, 1000+ requests per second (RPS) is ideal. Here's some performance comparisons to our HTTP counterparts.

Django, the least performant HTTP ASGI framework can serve ~1,500 RPS. Blacksheep, the most performant ASGI framework can handle ~10,500 RPS.

Archmonger avatar Dec 26 '21 09:12 Archmonger

A quick test of using pyjion.enable() within dispatch_single_view has shown performance degradation, likely due to the async with limitations noted in their documentation.

Archmonger avatar Dec 26 '21 09:12 Archmonger

There's definitely a lot of room for improvement. The areas to focus on, probably in this order, are:

  1. Computing JSON patches. Right now this is using a pure Python library that I've come to notice isn't very actively maintained.
  2. Find and optimize the hotspots in rendering VDOM models (see idom/core/layout.py).
  3. Server-side rendering (i.e. serve the initial view as static HTML).

rmorshea avatar Dec 26 '21 22:12 rmorshea

JSON Patch

I tried doing some research, seems like there isn't anything else worthwhile on the python side. This is a promising variant on NPM.

This raises a question of whether it's worth the performance to compute a JSON Patch in Python as opposed to resending the whole JSON content body (and calculating the patch client sided).

Internal Layout

Just during my digging I've already seen a couple of situations that could be migrated to list comprehension, which is significantly faster than python loops.

SEO Compatible SSR

Implementing SSR is definitely going to complicate IDOM core. SSR may actually reduce IDOM performance. Everything happening on the client side isn't blocking the WS queue.

This is a big topic to tackle and deserves a discussion on its own.

I'm not even sure where I'd say we add this to the roadmap. Priority of this card depends on long term goals and focus. I'd personally rank it pretty low on the list.

Roadmap

I'd like to work with you on fillling out GH Projects to provide visibility on version goals.

We should also tag up to discuss OSS funding. IDOM is definitely something that I'd be able to pitch.

Archmonger avatar Dec 27 '21 03:12 Archmonger

Yeah, scrap what I said before, we need to profile before doing any work. It's very possible that using JSON patch doesn't provide any significant benefit in the most common use cases.

To the roadmap, yes let's find a time to coordinate that. There's actually a roadmap page in the docs I haven't update in a while.

And for funding, I suppose the question is what it would be for. I'm happy to get reimbursed for the time I already put in, but I don't have more to give, so adding money won't make things go faster. It would be more valuable to find a way to encourage outside contributions, either from yourself or others.

rmorshea avatar Dec 27 '21 07:12 rmorshea

See https://github.com/idom-team/django-idom/issues/31 for a relevant performance issue that will eventually need to be resolved.

I believe it's going to be incredibly important for IDOM to support async components. In the case of Django, users will experience WS performance degradation due to blocking the websocket event loop.

Archmonger avatar Jan 02 '22 12:01 Archmonger

We might get some performance benefit by pushing ReactPy into it's own dedicated backhaul thread (containing an asyncio loop). One thread per ASGI worker, which we can initialize within reactpy:__init__.py.

That way, rendering components won't prevent the webserver from doing webserver things.

Archmonger avatar Jul 10 '23 09:07 Archmonger