zulip / zulip-mobile

Zulip mobile apps for Android and iOS.
https://zulip.com/apps/
Apache License 2.0
1.3k stars 656 forks source link

Use good data structures in Redux state, to avoid quadratic work #3949

Open gnprice opened 4 years ago

gnprice commented 4 years ago

Priority checklist (with updates from our progress):


As discussed in #3339, there are a lot of places where we maintain large amounts of data -- like all the users in the realm -- in data structures like Array which make it inefficient to find the data we want at a given time, like a particular user.

Some other data structures are maintained as objects used as maps: notably, the collection of all the messages we have from the server. These are efficient for lookup... but both these objects-as-maps, and the Arrays, are extremely inefficient to build up for large amounts of data in the Redux style we use. Specifically, building a new array like [...state, newItem] or a new object like { ...state, [newKey]: newItem } has to copy the entire existing array or object, taking linear time -- which means building up an array or object with N items this way takes quadratic time O(N^2). Demo at https://github.com/zulip/zulip-mobile/issues/3339#issuecomment-462963242 .

So, we should fix that.

There are a couple of potential stages here:


The main technical obstacle I believe we'll need to resolve is how to serialize and deserialize these data structures for redux-persist. This shouldn't fundamentally be complicated once we work out how; but it'll require some studying of the docs of redux-persist and friends and of Immutable.js, then possibly of implementations where docs are inadequate, in order to see how to wire up appropriate serialization and deserialization functions. Relatedly, we'll want to think through and test the migration path for data serialized by a previous version of the app.

I've forked off #3950 for using Map in place of object-as-map (the first stage above), which will also be an opportunity to figure out this aspect.

A quick extra note, in addition to what's there:


Specifically about Immutable.js: one thing I remember taking from when I was reading about this area last year is that people get annoyed with converting their objects to and from Immutable's types.

My thinking on that -- untested, so maybe this is hard for some reason -- is that that could be addressed by

Within this repo there's some ancient history with Immutable, from 2016: it was used at first and then was ripped out, in a series ending at 2eb654f41. It looks like the strategy there was to use it even for the struct-like objects inside the collections; as expected, this meant its API showed up all over the code, and the people working on the app at the time decided they found that too annoying to keep doing.

Previous related chat discussion here: https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/pure.20functions/near/793927

rk-for-zulip commented 4 years ago

In particular, for data which we want to keep sorted (for example, our NarrowsState, which is sorted by message ID), we may want to consider functional-red-black-tree.

gnprice commented 4 years ago

With #4047 completing #3951, I think we've now largely done the hard part for this! We're using remotedev-serialize in storing our state.

The next piece is to start converting pieces of our Redux state. I'm not sure we need #3950 as a further intermediate step; probably should just start trying migration straight to Immutable and see what obstacles come up.

gnprice commented 3 years ago

And after #4201, we're now using Immutable.Map for state.narrows!

Another instance of this will be #4252, for state.flags.

In general I think the highest-priority places to do this are roughly:

Then another category is where the data structures can get big but aren't so often updated:

chrisbobbe commented 3 years ago
  • state.messages is a map with one element per message. This should be an Immutable.Map.

I'll do this next!

gnprice commented 3 years ago

We've continued making progress on this. I've made a checklist at the top, mostly from https://github.com/zulip/zulip-mobile/issues/3949#issuecomment-744164250, and checked off the items that are done. Still more to do!