• Thinking out loud again: If unloading/eval becomes more common should the unloading function of apps be exported and be accessible by other apps? This way e.g. Quick Launcher or Pattern Launcher could utilize/facilitate faster app switching as well?

    I've nearly got remove/unload and eval working between BWClock and Desktop Launcher correctly on my app loader now. There's still some small buildup in RAM usage though.

    EDIT: BWClock code. Desktop Launcher code.

  • I think as a first step the implementation as a remove handler set for setUI is probably enough. It should be possible for apps like quicklaunch to just call setUI() to trigger the removal of the currently running main app.

    I did a small demonstration of the combination of UI removal, @Sir_Indy s hidable widget bar, modified gpstrek/iconlaunch apps and widgets implementing an update method. This may be the loading-screen-less future ;)

    Not yet bug free, far from optimal but very cool to see the Bangle be about as fast as my old Amazfit BIP S.


    1 Attachment

  • That makes sense. Nice demo! :) The Bip is in the back of my mind as well ;)

  • I think setUI is probably enough for now too.

    It's worth adding though that while this is cool - I really don't think we should rush to implement this all over the place (particularly app switching). The benefits of saving ~0.5 sec changing apps would be easily wiped out if it in any way caused the watch to become unstable

  • ...The benefits of saving ~0.5 sec changing apps would be easily wiped out...

    Yes, from my own tests not entirely managing to mitigate buildup in RAM use I absolutely see your point. But I am eager to have my bangle be faster with app switching in particular 😛 that's really the one area where it feels slow - within apps it's plenty responsive for my tastes.

    What do you think of the route taken with the latest update to Icon Launcher where there's now the option to switch "fast app loading/switching" on in settings? Is it safeguard enough to have the default be regular load()-calls with the option of faster/likely less stable eval()-calls also for more apps? Or do you feel this could be more trouble than benefit as well? 🙂

  • Yes, from my own tests not entirely managing to mitigate buildup in RAM

    If we actually know it doesn't unload completely and will cause instability eventually, I'm a bit unsure about it even being in the default Icon Launcher behind an option. Of course if you want to do it in your own copy of the app loader then that's great - but I'd much rather that anyone interested tries it out there knowing what they're letting themselves in for, than a normal user turns the option on without really understanding what the implications are (if you see an option titled 'fast launch' you're probably going to turn it on).

    Personally, I feel like there are a few other ways to improve experience, like switching to using the on-chip flash memory access hardware (currently we just use software). I'd much rather get stuff like that done and improve performance across the board than spending time trying to work around it.

    should the unloading function of apps be exported and be accessible by other apps? This way e.g. Quick Launcher or Pattern Launcher could utilize/facilitate faster app switching as well?

    The issue here is it's not just clock -> launcher -> app but maybe clock -> launcher -> app -> app -> app -> app -> etc and so any leaks will really add up.

    These kind of things would work great in a controlled environment, but Bangle.js App Loader is anything but a controlled environment. Code quality of apps on average isn't great as-is, and I honestly don't ever see a point where we could happily say that every app will clean up after itself.

    ... and even if we tagged apps that did clean up after themselves, I think it's unlikely that each contribution to an app in the future would be checked to see that it didn't leave memory behind.

  • All good points, especially about prioritizing doing things 'right' instead of having workarounds if they aren't needed. Thanks for elaborating.

    If we actually know it doesn't unload completely and will cause instability eventually, I'm a bit unsure about it even being in the default Icon Launcher behind an option.

    'My tests' referred to the modified BW Clock and Desktop Launcher on my app loader. I haven't tested Icon Launcher for RAM use buildup - but I suspect @halemmerich has done a better job with that than I have with my tests ;)

    And again, I can just look to my own code and ability to understand retaining stability is a real issue.

  • I too would prefer @Gordon s resources to be spend on improving system wide performance. There are lots more people doing experimentation on the JS side of things than on firmware level.

    My daily driver bangle install currently needs 1.6s of time between the first line of .boot and the first line of the default launched clock after reboot. Switching from clock to launcher currently needs 3.83 seconds for me, which feels like a LOT. It is actually the most remarked on thing if I show the bangle to other people. Fastloading reduces this to basically nothing in case of apps without significant own loading times. Worst offender beeing imageclock adding at least a few hundred ms to those nearly four seconds ;)

    I created my fair share of memory leaking boot code, so the problem generally is not a new one :)

    The problem for fastloading can be split in a few parts. Some are not as critical for memory leaks.

    Clock <-> Launcher: Should be relatively easy to check for memory leaks during development and is one of the most done transitions. This works well on current firmware with setUI-remove and the eval style of loading the clock.

    Launcher -> App: The only point that should not be leaking memory is the launcher, since it needs to remove itself to free up memory for the app. The app should not see any difference to beeing loaded after the normal boot code then.

    App -> Launcher/Clock: Technically the same as launcher to app, but a lot more apps to check and validate. Modification of all apps and keeping them clean in regard of memory use is not really feasible. Current state of affairs is a reboot of the system if no remove-method is implemented. This also mitigates smaller leaks by rebooting every few app launches. This waiting time is not really a problem, since closing an app often means stopping use of the watch anyway. At least for me the delay after using an app is a lot less impactful than the waiting for an app to open.

    Widgets: This one is currently not that easy to solve. I have experimented with tracking which listeners and timeouts etc. a widget registers, but trying to clean that up later opens a whole can of worms that probably better stays closed (think reused timeout numbers).

    I think some change in handling the widgets would be nice, maybe returning the widget as an object from the wid.js with some conventions on how to structure them could work well (a bit like the recorder modules are loaded and added). This would also allow to return a stub object that loads the widgets code from a file on first call to keep the memory use minimal in case an app does not loadWidgets. This would solve some things like the widget name not matching the file name or widget JS loading no or more than one widget on some condition (not easily recoverable from if widgets are not filterable for this behaviour).

    My current experiments include a fastload-app, which modifies the Bangle object to add some handling for unloading widgets and additionally widgets need to implement a remove method to properly work (at least those that use timeouts/listeners etc.). An optional update method is used for notifying widgets of changes like Bangle.CLOCK.
    This seems to work well for apps with and without widgets. Could be more efficient, since it effectively hard unloads and later reloads all widgets if an app does not call loadWidgets immediately. There is of course the problem of not (yet) modified widgets, but this could be handled by disabling fastload in this style if one of those is loaded. I'm sure there are still lots of edge case that I have not yet triggered.
    Main magic hides here: fastload.boot.js

    Current state of experimentation in my apploader:
    Firmware: at least 2v15.20
    Clock: Anton
    Fastload enabler:
    fastload
    Modifed for fastloading:
    iconlaunch
    gpstrek
    widclk
    widbat
    widlock
    widbars
    widcloselaunch

    Not really needed for fast loading but very nice effect:
    @Sir_Indy s slightly modified widhide for swiping the widget bar from the top on clocks.

    But all of this might well be a case of:

  • Switching from clock to launcher currently needs 3.83 seconds for me, which feels like a LOT.

    Ouch - yes! I'd be really interested if you could figure out what's taking the time there.

    But yes, I think the clock -> launcher transition is one where it does make sense to try and avoid the load(). Even if there are leaks at least it only happens once at the moment so won't cause a huge build-up.

    With widgets: loading these can take a decent amount of time, and personally I think trying to handle loading/unloading them automatically when switching apps is just a step too far. If you're not careful you end up doing so much extra work that even after all this, load times will not be significantly better than if you just did load()

  • My tests with implementing a remove method on widgets work well but would need implementation in all widgets. Maybe an alternative to this would be keeping the widgets loaded, but hiding them in apps not loading widgets? RAM could be an issue, but at least on bangle 2 it seems to be not that problematic for most apps.
    A lot easier to implement and probably all of it in a bit of boot code. Actually really similar to what widhide does right now.

    As far as the starting time goes, the biggest block is the quicklaunch app at about half a second for scanning the 40+ apps I have installed. The rest is not that big, but there are some apps that need a bit more time. Sensor tools and BTHRM have a lot of code. I do not know where the 2.23 seconds not explained by the 1.6 seconds from .boot are from. There could be some time from initial drawing and loading widgets for the launcher in there. I timed from the first frame showing the loading screen to the icons from iconlauncher showing.

  • Maybe an alternative to this would be keeping the widgets loaded, but hiding them in apps not loading widgets? RAM could be an issue,

    How many apps don't load widgets, and why? It might be best to just slow-load() those apps.
    I can't think of a good way to determine if an app is going to load widgets though :-(

    scanning the 40+ apps I have installed.

    It looks like quicklaunch could be improved a bit to not do this every boot, but just check if an app exists before loading it.

    Maybe we should add a debug bootloader that logs per-bootfile timings to the console on boot?

  • Maybe an alternative to this would be keeping the widgets loaded, but hiding them in apps not loading widgets?

    Yes, I think that would be best - if it were implemented in a library it should be pretty easy to include.

    Wrt timings:

    • As far as I can see quicklaunch really has no need to load the whole apps list at boot. It might as well just check that appname.info exists when it's time to load the app.
    • BTHRM is huge and I imagine just parsing that takes a while - there was some talk of trying to strip down what goes into the boot code (or maybe resurrecting the old one as a lite version? the original has 70 lines of boot code, not 630).
    • I just looked, and again sensortools is another 350 lines of boot code so that won't be quick either.
    • You say you're timing to iconlaunch displaying? If quicklaunch is that slow then I guess iconlaunch loading every app's icon file before it displays everything (launcher does this too though) could really slow it down. How many apps do you have?

    I suppose it could be an option to extend the bootloader code such that there was an option to show the load times of each section?

    I think my concern is your watch is slow because there's a huge amount of extra stuff that's been added to it. Personally, I feel like rather than adding more code to try and work around that, it'd be best to work on finding out what's slow and then optimising those bits, because I think there's some really low hanging fruit there.

    For instance if the launcher is slow, potentially there might be a way of pre-parsing all the apps into a single JSON file (much like the bootloader adds everything to one file and uses .hash to check for changes). Icons could also be loaded as-needed since you only need to load the first 9 before you display the first page of the icon launcher?

    I'd have thought that with a bit of work between the various apps/widgets/boot code you could maybe even double boot speed, and that would then apply to everything

  • My 2 cents: It would be great if the launcher and app loading times could be reduced. At the moment, a long time Loading ... most annoying in daily use.

  • I just pushed some changes to the development app loader to enable an app cache in the default launcher.

    • In a default-ish install it brings the startup time down from 0.46s to 0.36s
    • I installed a few extra apps and it came down from 0.57s to 0.39s (it scales much better)

    And if you're switching apps so not reloading widgets it's even faster (0.25s). I feel like the same changes could be pulled into other launchers pretty easily too, and would go a long way towards improving the app switching times when there are a lot of apps installed.

    Maybe we should add a debug bootloader that logs per-bootfile timings to the console on boot?

    Try this: https://gist.github.com/gfwilliams/95f77­6176929b58b3603034065c3ec2d

    Seems to work ok, by default I'm seeing:

    
    init 0.06207275390
    health.boot.js 0.01016235351
    sched.boot.js 0.01272583007
    welcome.boot.js 0.00277709960
    

    So not too bad at all.

  • Loading the widgets and manipulating the loadWidgets/drawWidgets/draw-methods to just do nothing in case an app has not loaded widgets seems to work a treat. I believe the only way for widgets to disturb graphics is now drawing outside of the draw method. This could be handled by giving the widgets a dedicated buffer like widhide does.

    I have implemented some changes for logging boot times:

    Boot times:
      sensortools.0.boot.js: 302ms
      bthrm.0.boot.js: 151ms
      agpsdata.boot.js: 14ms
      android.boot.js: 61ms
      calibration.boot.js: 17ms
      fastload.boot.js: 17ms
      health.boot.js: 44ms
      owmweather.boot.js: 68ms
      qmsched.boot.js: 24ms
      quicklaunch.boot.js: 622ms
      sched.boot.js: 49ms
      swscroll.boot.js: 11ms
    Boot took 1592ms
    

    So actually BTHRM is not even that bad ;)
    Sensortools is easy to fix, just moving everything into a lib and requiring only if enabled should help. BTHRM not that easy, tried 2 times now and did not manage meaningful size reduction without losing features I want. Quicklaunch is probably easy to fix as @rigrig said.
    Iconlauncher takes about a second to load on my 40 app bangle, probably relatively easy to alleviate that by loading apps/icons as needed instead of all at once as you suggested.

    But, currently I can with very little effort remove these 1.6 seconds altogether by just not needing to execute the boot files between app changes (or at least from launcher to app). Besides that I think optimizing single apps is currently more important than fastloading, even if I personally like the efficiency of not tearing down the environment for every app change. That will probably bring down the time to around half a second on a big install like mine. Probably good enough for most usecases.

    Comparison between nearly empty bangle on 2v15 without fastloading and 2v15.40 with fastloading shows a nearly halved time from clock to launcher and about 10% faster the other way round (Anton and Launch). Although in this setting both seem to be very similar to the naked eye.
    Espruino itself seems to take roundabout 80ms between load() and the first line of .boot0

    There remains one point where fastloading has a real benefit:
    Not losing state in BT and sensor stuff. Reconnecting in BTHRM takes some time and I think keeping a gps fix alive is also beneficial in comparison to aquiring it new on every app change. If I use GPS I currently think twice about changing the app as not to loose a fix which I then sometimes can not get back.

    I will keep playing around with the fastboot idea in my apploader and try to get some PR in for low hanging fruit :)

  • I took your caching code and integrated that into iconlaunch. Updating the cache takes about 1.5s while using the cache "only" needs 500ms. Pretty awesome improvement, would not have expected this.

    Finding the launcher every time is also slow. I have implemented something similar to what is done for clocks with the possibility to configure one in the settings. That takes the time for finding the launcher from 194ms down to 7ms on my close to stock bangle. Probably a lot more on the other one.

  • Thanks! Just merged (and also merged your similar changes for the clock) and the builds now with up to date JS really do feel a lot snappier.

    Now we just need to ensure that more clocks implement Bangle.setUI({..., remove}) - just a note that I tried adding it to the ClockFace module but clocks like terminalclock add a lot of other stuff which caused it to break. I guess we could make it opt-in

  • There remains one small thing to get the current state working correctly with regard to widgets.
    Currently some widgets prevent themselves from being loaded depending on Bangle.CLOCK.
    I have checked and only found widclk,widclkbttm,widclose and widcloselaunch to have this problem. We could solve this by just checking Bangle.CLOCK on every draw and react accordingly or we could define widgets to have an update method to tell them to recheck their environment.
    Implementation on the Bangle side of things could be done by calling all available update methods on widgets at the end of Bangle.setUI and Bangle.loadWidgets. Update method is better when widgets change their width depending on CLOCK and prevents additional calls to drawWidgets which would be needed when draw changes the width.

    The small negative impact of this would be loading the 4 mentioned widgets even if a clock app is active. Since they are all on the order of 10 lines of code this is probably not a problem. Solving the problem for theoretical future big widgets would be possible by having the update method un/load the big pieces of code on demand.

    I have tried the update method way and have not found any problems with it the last few days.

  • The update method sounds good to me (I'm not sure if we could document that anywhere?). Or given the relatively few widgets that do this, we could just check in the draw method and then queue a call to drawWidgets if it changed?

    if ((Bangle.CLOCK==true) != (WIDGETS.widclk.width==0)) {
      WIDGETS.widclk.width = Bangle.CLOCK? 52 : 0; 
      return setTimeout(Bangle.drawWidgets,0);
    }
    

    I seem to recall there are some widgets that do similar things already.

    Using extra RAM is a bit of a shame on Bangle.js 1 but I guess it's not much - and actually adding an update method would be worse than the above

  • Every widget that changes it's width during draw needs to schedule a redraw. This seems somewhat wasteful since at least with simple timeouts all calls will eventually be executed. The update methods can all be called without needing to schedule additional draws. Drawing in that case only needs to be done if an app actually calls drawWidgets.

    The current state of affairs is at https://github.com/halemmerich/BangleApp­s/tree/experimental/widgetUpdate and my apploader at https://halemmerich.github.io/BangleApps­/

    There are now two apps that should work indepent from each other: fastload and widgetupdate

    fastload mostly wraps load to automatically decide between reboot and fast loading. That keeps the apps a tiny bit simpler. The rest just basically wraps methods to be able to show a different loading screen and add handling remove-methods in the E.show* methods. This will probably stay an app on my apploader for the foreseeable future and I suspect to have missed a lot of edgecases.

    widgetupdate is a bit more interesting as it handles the hiding of widgets (including wrapping touch,drag and swipe handlers) as well as setting a correct appRect and detection of an app loading widgets or not. That should work for all apps without changes in the app. This also does essentially the same thing for the drawing as the newest widget_utils module does but at widget loading time. Newly available methods are Bangle.updateWidgets() and Bangle.hideWidgets().

    There also is a small modification in bootloader which adds Bangle.showClock. That is used in widclose and launch to get the clock loaded while beeing able to be modified by fastload if installed. Essentially this prevents using eval there, which can not be overriden to modify its behaviour. It always calls load, which is then optionally overriden by fastload with the eval approach depending on the existence of Bangle.uiRemove.

    Maybe some of the widget handling could be integrated into the firmware to fix the current bad behaviour with regard to Bangle.CLOCK changes.

    Alternatively the fast loading using eval from clock to launcher could be reverted to get the widgets working like they were before and then optionally reinstated by installing fastload and/or widgetupdate. That would however mean implementing remove handlers in watchfaces/apps and update methods in widgets that are not used if those two apps are not installed.

    TL;DR: To try it out install bleeding edge firmware, install fastload and widgetupdate ,update everything my apploader has newer versions of and probably expect bugs (especially messages is currently practically not tested at all) :)

  • I gave it a spin! A video of me navigating the watch with presses of the physical buton, then using Quick launch, and doing some other stuff, is attached.

    It's a bit inconsistent with it sometimes displaying 'load' message, sometimes 'fastload' message and sometimes no message box. The most consistently inconsistent (😛) thing with regard to this is when I go back and forth between clock and launcher using the Quick launch app for swipes.

    I also noticed that if I had 'Widget editor' installed the widgets would not be rendered graphically, but could be pressed (e.g. back button).

    Used both fastload helper and widgetupdate.


    1 Attachment

  • Spotify remote does not implement a remove handler for setUI so normal loading is expected. Quicklaunch replaces the setUI function on clock faces and when fastloading to somewhere else that is not cleaned up until a normal load. That is probably the reason for changing behaviour.
    Widget editor throws the loaded and modified widgets away and reloads everything manually. That probably creates problems with the wrapped draw methods. There should be a way to resort the existing widgets objects to prevent that.

    It seems there are still some things to catch :)
    I have today seen a problem where menus are functional, but sometimes not updating the display correctly. It seems there is one draw on tap missing. Scrolling up and down then shows correct content.

    Edit: D'oh! Forgot to return the resulting object when wrapping E.show*, will fix that this evening.

  • I think there's a bit of a worry with what you're doing with E.showMenu/etc. For a simple app that just shows the menu it's fine, but imagine something like https://banglejs.com/apps/?id=espruinote­rm where it's done something (opened a bluetooth connection in this case) and then shown a menu. Now when you want to switch apps you'll just move right on to the next app without clearing up.

    This looks great as a proof of concept and it's really cool to have it on https://halemmerich.github.io/BangleApps­/ for everyone to use if they want to, but just to warn you that I think right now this is just too much to try and merge back into the default app loader.

    Even the changes to individual apps to make them compatible can be bad. For instance I just checked the messages app...

    • Default messages app -> loads in 248ms
    • messages app wrapped in (()=>{ ... })() -> 370ms
    • messages app wrapped in ()=>{ ... }() as done here -> 420ms (it's not valid JS so can't be minified)

    The reason is when you just have the app on its own, it can be executed in one pass straight from flash. But when you wrap it in a function the whole function has to be parsed from flash and then read again a second time when it's executed.

    If the function wrapping is to stop functions getting defined outside of scope, I think changing: function myfun(...) { ... } to let myfun = function(...) { ... } should have the desired effect.

    I'm all for making Bangle.js faster, but I don't want to be in the position where I'm making it slower for everyone else just so that it can be faster for those that choose to install fastload.

  • I think there's a bit of a worry with what you're doing with E.showMenu/etc. For a simple app that just shows the menu it's fine, but imagine something like https://banglejs.com/apps/?id=espruinote­rm where it's done something (opened a bluetooth connection in this case) and then shown a menu. Now when you want to switch apps you'll just move right on to the next app without clearing up.

    That is why I tried to add the remove handler to the options given to E.show*. Apps then can cleanup after themselves if I decide to leave them when they are in a menu. The default is still not cleaning up and loading normally. See setting a remove handler for the scroller in launch to make fastloading out of it possible https://github.com/halemmerich/BangleApp­s/commit/74d0499976bbeb845ffda4a4fbb15ca­40884bc18.

    This looks great as a proof of concept and it's really cool to have it on https://halemmerich.github.io/BangleApps­/ for everyone to use if they want to, but just to warn you that I think right now this is just too much to try and merge back into the default app loader.

    I think we need at least a solution for the widgets not beeing updated correctly either in draw methods (multiple calls to Bangle.drawWidgets() => slow) or with update-methods in Firmware (part of what widgetupdate does). The rest I am completely fine with keeping separate.

    For instance I just checked the messages app...

    You can completely ignore that, there I just tried to have a look at how far I could go without completely borking everything. I will probably revert that completely since it is massively complex to even test every feature. Preferred solution to that would be an alternative implementation like is discussed in https://forum.espruino.com/conversations­/375794/.

    If the function wrapping is to stop functions getting defined outside of scope, I think changing: function myfun(...) { ... } to let myfun = function(...) { ... } should have the desired effect.

    Yes, tried that for some apps and works fine. Didn't think about the code reading/parsing implications of wrapping in a function.

    I'm all for making Bangle.js faster, but I don't want to be in the position where I'm making it slower for everyone else just so that it can be faster for those that choose to install fastload.

    No, fastload must be optional and as free of changes for all other stuff as possible. In the end normal loading will probably be the default for a long time. But stuff similar to fastloading will happen inside of apps (showing menus, scrollers and using setUI() for different parts of the app etc.) and I think we should have working cleanup for those scenarios.

  • I have done some changes to quicklaunch and wid_edit. Those should help with the erratic behaviour. I had not yet used wid_edit before, but I think it still works.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

POC - Combining BW Clock and Desktop Launcher to spend less time "Loading..."

Posted by Avatar for Ganblejs @Ganblejs

Actions