-
-
Notifications
You must be signed in to change notification settings - Fork 938
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
Improve weather service #1822
Improve weather service #1822
Conversation
Make use of a template function to get the most recent event of a specific type.
Build size and comparison to main:
|
Great point.
It really shouldn't, I did try and make sure everything is checked, closed and cleaned up (according to the documentation). Adding timeline support to InfiniSim using the same library might shed some light though. It's way more likely there's simply not enough truly free memory to actually accommodate the currently allowed timeline size.
I did initially consider an array per type of event (because different types of events have different-sized data structures), but that's IMO way more wasteful. |
9b4f7b4
to
46c5641
Compare
Yeah, but I think part of the issue is also that currently there are instances where we essentially allocate twice the memory we need, e.g. vector is at capacity and a new item is pushed. imo it would be more transparent to have the containers be statically allocated and be smarter about when to add events. Sure this will increase flash size, but when the alternative is to have alloc fails when loading font or something similar I'd rather not have the most up to date weather :^).
Maybe I'll give that idea a spin. Again this will use more flash, but it will be compile time visible how much the weather service actually needs. |
Not just flash though, it will consume that amount of RAM (permanently). Which is already spread really quite thin.
The intent is to extend the timeline to contain events of other types as well. Such as calendar events or reminders, but we'll have to carefully consider if we can afford that. |
Before further parsing of the packet, check timestamp and expires field right after it has been decoded to avoid unnecessary allocs/frees. Also perform tidying up of the vector before attempting to add any new elements.
46c5641
to
d29f89e
Compare
Would love that one. However I think it might be smart to have a fixed max size for the vector, thats tried and tested to leave some breathing room for other allocations :^). Also maybe there is a way to tune the |
395003e
to
cf21190
Compare
cf21190
to
3a40c0b
Compare
@Avamander I think to validate strategies the best course of action would be to gather "real life" data at what point in time the app pushes which data and do some simulations on the vector behaviour. I checked the app and it offers protocolling functionality but it does not log the expiration and the fields "verbatim" as the bluetooth communication uses it. There is some work to be done to change the logging a tad bit. Things that I think of adding:
What do you think? |
I have a (semi-informed) question: |
@minacode any input is welcome :^). I think this is maybe possible, to store the data on the external flash. My thoughts on this:
Eventually if we ever retrieve calender events storing the to external flash is promising so they would persist across reboots. For the weather I am unsure if that makes sense. @Avamander @JF002 @minacode |
Thanks everyone for this analysis so far! I'm now joining the party! I've just had a look at
All in all, we can see that So, how can we fix/improve this? I know that this timeline implementation was designed to be as generic as possible. We are currently using it to store weather data, but the bigger plan was to use it for other functionalities like navigation directions, agenda events,... which is a good idea! However, now that we face those memory issues, the current implementation does not seem optimal due to its memory usage and the limited amount of RAM memory available on the PineTime. I guess we can explore a few options
As an example, I designed the notification functionality with strong limitations : max 5 notifications of max 100 characters. This implementation is not the most effective one since we cannot store 10 notifications of 15 characters even if there's theoretically enough room for them, but it's at least simple and deterministic : it uses 500B of RAM memory. And since this memory is statically allocated, it'll never interfere with another feature. I'm not saying we should implement the weather feature this way, but I think that compromises and limitations are probably unavoidable here. Let's have a look at the current usage of the weather data in InfiniTime : we only want to display the current temperature and precipitation. The timeline allows the companion app to send multiple events (of the same time) with different expiry time, but we currently use only the 1st valid event available in the timeline. I know that we could use more functionalities and data in the future, but maybe we should think about which data we'll realistically use. Do we really think we'll display all these data? Or, put another way, maybe we should decide which data we really want to handle, and remove the other ones. I'm also wondering why would the weather companion apps send 98 events for a single update? Do all these events contains actual data? Even if I think InfiniTime should be able to "defend" itself against a companion app sending too much data, there are maybe a few improvements to be done on the companion app side? |
Sorry if I did not phrase it correctly, but what I meant is that at one point in time there were 98 elements in the vector. A single update from the TWFG app yields ~6-9 events.
+1 for me on this one. For now I think keeping the value closest to the current time would be sufficient, but I do not know if we reject the others if the apps pushes previous values again? |
That seems to be indeed quite excessive and ideally we'd handle this misbehaviour.
That would be a nice addition.
Especially with longer string content (descriptions, full titles) this seems like a logical approach. Though it wouldn't hurt too much to periodically flush the entire timeline to storage, deallocate the entire list and then reallocate. This could reduce fragmentation, in theory.
This seems like a fairly simple and safe change, though it might need some testing.
That's a large allocation for a feature that might not be used. It would also mean a custom allocator (considering the different-sized elements), which is a significant amount of complexity.
Certainly. Though it seems quite weird there are that many events at the same time. Has anyone gotten a dump of all the events?
We could possibly discard the ones we currently certainly don't use, but the compatibility exists for the explicit reason to make it possible to write things that do use this data. Plus this wide compatibility has now revealed certain assumptions, such as well-behaving companions, to be false. I wouldn't make large alternations to our current approach, rather fortify it against abuse (upper sanity limits), improve storage (deduplication, packing) and data usage (which events do we use, clearer instructions about which data should be pushed and how). |
Since discussing abstract ideas is quite difficult I opted to go for a more methodical approach. I want to actually see what is going on in regards to memory consumption etc. without having to build the watch firmware, reflash it and wait. I modified the Gadgetbridge app to log the data send to the watch as a json. In parallel I started work on a benchmarking simulator for the behaviour where I can plot different implementations against each other: The idea is to take the heap allocator from infinitime and make it available as a C++ allocator to be used with STL-containers and smart-pointers. Overriding the malloc/free symbols globally I avoided on purpose since this might capture unwanted test infrastructure noise. Anyways the plan is to feed the captured data into the simulators which will yield effectively this:
This will give an actual point of comparison for different implementations, which should be close to (if not identical) to the watches behaviour. |
Thanks for continuing the analysis! Your new testing method will probably provide valuable data! I do plan on having a closer look on this issue soon so we can try to figure out the best solution for this issue! |
I added a few debug logs to see what exactly happens when weather data is received by WeatherService. For each event, I log the following info:
Here is the complete patch with those logs : Debug_weather_service.patch.txt I ran the following tests using Gadgetbridge 0.75.1 and Tiny Weather Forecast Germany 0.61.0. Shortly after connecting the watch, 7 events are received and 296B were allocated :
Then I hit the refresh button in TWFG: 13 events & 552B
With those test, I can confirm that
But the following points struck me first:
With those observations in mind, I think we do a few easy things to reduce and keep under control the memory usage of the weather service:
This should fix the issue on the short term : the memory usage will be reduced by a lot and should not grow uncontrollably. And this will give us more time to improve the current implementation to reduce the number of allocations, the storage overhead, and ensure that the memory usage stays under control when we add new event types. What do you think of those suggestions? |
I wanted to investigate this issue:
#1788
I found some things that could be refactored, and also some issues with the current implementation:
Further investigation needs to be done here: