jmoenig / Snap

a visual programming language inspired by Scratch
http://snap.berkeley.edu
GNU Affero General Public License v3.0
1.5k stars 744 forks source link

Loading saved stuff from XML is slow #296

Open Muon opened 10 years ago

Muon commented 10 years ago

Loading saved stuff (e.g. tools) is slow. According to langtech/snapapps#71 it takes more than a minute to load the tools on an iPad, and the Acer C7 Chromebook I'm writing this from takes 8 seconds with my patch in 933471e. I didn't see any obvious points for optimization apart from that, but a lot of time seems to go into canvas drawing (stroke(), specifically). Can we reduce that?

nathan commented 10 years ago

Yes. I've worked on this in the past, but it gets pretty messy when you start avoiding constructors and manually initializing stuff so that you do fewer redraws.

brianharvey commented 10 years ago

@n: This is a solved problem: http://www.amazon.com/The-Metaobject-Protocol-Gregor-Kiczales/dp/0262610744/ref=sr_1_1?ie=UTF8&qid=1389545818&sr=8-1&keywords=metaobject+protocol

nathan commented 10 years ago

This is a solved problem

Huh?

(also, I'm @nathan)

brianharvey commented 10 years ago

Metaobject protocols allow a clean abstraction while also allowing the caller of an object('s method) to give "advice" in the call that allows the method to take advantage of special-case efficiencies. I didn't mean it's solved in Snap!.

xtitter commented 10 years ago

That link refers to "users of CLOS". They probably believe in the abominable snowman as well!

Am I understanding correctly that the palette is getting redrawn on every block definition created during the XML load? Or, is stroke() getting called at additional points in the xml loading?

jmoenig commented 10 years ago

:-)

Going meta on everything is cool, but not necessarily the "solution" to all problems.

When de-serializing a project, a lot of stuff gets drawn (using not just stroke() but just about any canvas API call that exists), not just the palette, but every sprite and every block in the whole project. And that takes time. We've discussed deferring the drawing until something is actually shown onscreen, but that's not practical, as - among other things - with first-class everything (especially blocks) you never know where and when they might show up eventually.

There might be other ways to make loading faster. One would be not to serialize anything at all but to actually dump the memory (Smalltalk images are blindingly fast), another would be to cache bitmaps for blocks and scripts and save them along with the project. But there are clear downsides to both of these approaches, the first one makes projects incompatible with future changes and the second one (actually both) blows up the memory footprint and bandwith requirements of projects. Nathan has at one point explored a third option that squeezes some milliseconds out of Morphic constructors, but as I understand this was more of an experiment and didn't result in a dramatic speed-up for loading projects.

And thirdly, we need a way to handle and debug projects, so all-too-aggressive optimizations aren't always in our best interests.

nathan commented 10 years ago

Nathan has at one point explored a third option that squeezes some milliseconds out of Morphic constructors, but as I understand this was more of an experiment and didn't result in a dramatic speed-up for loading projects.

That had nothing to do with serialization. I decreased the initialization time for all of Morphic's JavaScript by about 95% by using Object.create instead of new to create subclasses.

jmoenig commented 10 years ago

Right, but in doing so it it would also decrease the initialization time of all deserialized visual objects, so it would be a speedup and as such quite interesting.

AFAIK you didn't ever conduct extensive analyses or tests whether that would actually work throughout the whole system, as there are surely a lot of special cases in certain Morph's initialization code, or did you?

nathan commented 10 years ago

Right, but in doing so it it would also decrease the initialization time of all deserialized visual objects, so it would be a speedup and as such quite interesting.

No, it would not. Initialization time = time for each script to run. It did nothing to startup time for the IDE, nor did it optimize morph constructors.

jmoenig commented 10 years ago

Oh, so I misunderstood. I had the impression that your optimization was about avoiding running every init() method of every parent constructor and thereby avoiding redundancies - and possible canvas re-draws - by collapsing them all into a single method. Oh, wait, that wasn't what it was about, right? Now I remember, it was about initializing the constructors themselves, not the instances.

Muon commented 10 years ago

Well, this isn't about loading a project, it's about loading the tools, which are just exported blocks, no? Also, I'm not entirely sure yet whether the palette is being completely redrawn when a new block is added. (But my patch did reduce the number of palette refreshes.) The only thing I know is that when profiling the load of the tools specifically, stroke() showed up as the only remaining specific hotspot. The rest seemed quite spread out.

brianharvey commented 10 years ago

Oh, yes, if we can do something to speed up importing tools.xml, so that Jens will allow making tool loading automatic on startup, please let's!!!

nathan commented 10 years ago

I'll look into some of the more aggressive deserialize optimizations I was working on a while ago.

brianharvey commented 10 years ago

@jmoenig: Could we do the dump-memory or cache-bitmaps solution just for libraries (or even just for tools) and not for projects?

cycomachead commented 9 years ago

@jmoenig Reviving a really old discussion....

I was debugging loading a bad project and noticed that Watchers are also one example where there seemed to be extra work being done. Even though watchers were hidden, there was plenty of calls happening to fixLayout() and drawNew(). Wouldn't it make sense to not do that work since they won't be displayed? Watchers already have a flag internally, so it was / is an easy fix.

I haven't made a PR because I can't tell if I could be breaking something, but I don't think I have...

jmoenig commented 9 years ago

Sounds like good idea, Michael! Does preventing the watchers from calling these functions speed up loading for you?

cycomachead commented 9 years ago

Does preventing the watchers from calling these functions speed up loading for you?

On a small project, with a fast machine, it wasn't a huge advantage, but I reduced the functions by abou 600 on a project which only had one watcher...I'm planning on doing more testing soon. (Well one of the zillion things I think I need to get to soon.)

cycomachead commented 8 years ago

Can we call this fixed with 6061403a89bbbdd448a9086d3f443b5a5f88a330 ?

jmoenig commented 8 years ago

Let's leave this one open for now, because there is still a lot of room for additional optimizations in eliminating redundant redraws and layouts...

ToonTalk commented 6 years ago

In 4.2 I'm finding it can be very slow to import blocks. Frequently it is well over 10 seconds and Chrome asks if I still want to wait (sometimes multiple times).

https://ecraft2learn.github.io/ai/AI-Teacher-Guide/eCraft2Learn%20blocks.xml

It is 112KB and has about 100 blocks. Opening the page in the browser is fast so it isn't related to connection speed.

I didn't notice this problem before 4.2.

ToonTalk commented 6 years ago

And importing this project eCraft2Learn S4A.zip into FireFox took over 3 minutes (and 17 clicks on the "Wait" button that appears when a page is taking too long). Despite S4A being part of the name this is a pure Snap! project.