Scratch-blocks: Handling multiple sprites/workspaces

Created on 24 Jul 2016  路  12Comments  路  Source: LLK/scratch-blocks

We've had this discussion in a couple of places. Here's a slight attempt to consolidate the concerns and possible approaches.

Concerns:

  1. Performance and memory usage: it should be fast to switch between sprites, not require too much unnecessary memory when sprites are backgrounded. Sprites whose blocks are in the background shouldn't spend too much (or any) time handling events (e.g., a window resize shouldn't be handled 100 times with 100 sprites).
  2. General-purpose: we should be able to support situations where there are multiple sprites, but also just "multiple programmable things" in general. This should probably come naturally.
  3. Can support many: I would like us to support > 100 sprites fluidly.
  4. Dealing with diverging toolboxes/flyouts: we should assume that sprites/targets may have different sets of blocks, different categories, different variables and procedures. For example, the stage in Scratch 2.0 doesn't have many blocks that sprites do. And, sprites can have "local" variables and lists.
  5. Dealing with undo/redo: What is our design for this? Should undo/redo be within a particular sprite, or across the entire project? If across the entire project, should the workspace switch when an undo/redo happens in a different sprite? I might like that the best....
  6. Ease of development: solution hopefully shouldn't diverge much from current set-up.

Possible approaches we've started thinking about:

  1. Workspace is serialized and cleared when switching away; deserialized and loaded when switching to different target. Problems: current XML serialization is very slow, and storing blocks serialized in XML would possibly add a lot of unnecessary memory usage. Dealing with diverging toolboxes/flyouts seems like it could be messy this way. Pros: it would require almost no changes to the blocks; almost all the infrastructure could exist outside.
  2. Internal swap: we could try to extend the workspace to support multiple collections of blocks, multiple connection DBs, etc. This seems like it would have good perf/memory properties compared to serialization; it might be intrusive to develop.
  3. Each target has its own Blockly instance: then just hide/show each instance. We'd need to deal with events (e.g., the window resize) in a clever way to keep it performant. Workspaces may need to communicate with each other to deal with things like global variables. Potential memory usage questions.

Haven't really thought about how each of these interact with undo/redo. None of them seem particularly great, but one should probably work out.

Assigning @picklesrus @rachel-fenichel @thisandagain for feedback.

feature

Most helpful comment

Overview

Below is a simple distribution of both block and sprite counts to assist in understanding the performance expectations of Scratch projects. This data reflects the last 300,000 projects (both shared and unshared) as accessed on August 8th, 2016. The y-axis ("Number of Projects") is Log.

Block Frequency Distribution

Min: 0
Max: 3908
Median: 24
90th Percentile: 1463
Power Regression: 1.0293e7*x^(-2.0645)

scripts

Sprite Frequency Distribution

Min: 0
Max: 999
Median: 30
90th Percentile: 364
Power Regression: 2.0488e7*x^(-2.6774)

sprites

/cc @tmickel @cwillisf @rachel-fenichel @picklesrus

All 12 comments

undo/redo feels to me like it should be per-sprite--so when you switch to a specific sprite, you switch back to its undo/redo stack as well.

Dealing with diverging toolboxes/flyouts: we should assume that sprites/targets may have different sets of blocks, different categories, different variables and procedures. For example, the stage in Scratch 2.0 doesn't have many blocks that sprites do. And, sprites can have "local" variables and lists.

In Scratch 2.0 the sprites and stages have the same categories, but different blocks inside them. Is this likely to change?

Re: undo/redo. Agreed. Per-sprite seems reasonable. The alternative would be we'd have to switch between active sprite in the undo/redo stack. One unfortunate thing we have to think about is how undo/redo reacts with sprite adding/removing. When you delete a sprite, how do you undo that? Maybe there should be a separate undo/redo stack for that part of the editor...

I think it's probably safe to assume that the categories are consistent across sprites/stage/etc.

Overview

Below is a simple distribution of both block and sprite counts to assist in understanding the performance expectations of Scratch projects. This data reflects the last 300,000 projects (both shared and unshared) as accessed on August 8th, 2016. The y-axis ("Number of Projects") is Log.

Block Frequency Distribution

Min: 0
Max: 3908
Median: 24
90th Percentile: 1463
Power Regression: 1.0293e7*x^(-2.0645)

scripts

Sprite Frequency Distribution

Min: 0
Max: 999
Median: 30
90th Percentile: 364
Power Regression: 2.0488e7*x^(-2.6774)

sprites

/cc @tmickel @cwillisf @rachel-fenichel @picklesrus

@thisandagain @cwillisf @rachel-fenichel @picklesrus

Here is a starting spreadsheet of some benchmarking of the current state of things:
https://docs.google.com/spreadsheets/d/1rbHMnboLuq3IJUrfguIzyF81Syq1WWgK4B9cpo3RPrc/edit?usp=sharing

Summary:

  • 1 workspace and saving/loading: + memory usage, + page load time, - switching time.
  • Many workspaces and switching: - memory usage, - page load time, ++ switching time.

Re: categories changing between programmable objects - We've discussed some use cases around this, such as having multiple robots with different functionality or characters with different move sets. I'm not aware of anyone actually doing this, though, so low priority.

Thanks @tmickel !

I'm also curious about your two questions on the "Load / Save Blocks to 1 Workspace" sheet:

  • Why is clear() so slow?
  • Can we make this (load/save blocks to one workspace) faster by avoiding XML serialization?

In terms of "Workspace Generation Times", do we have an understanding of what that looks like in Scratch 2.0? 5895.5ms seems slow for 1000 blocks, but I'm not sure how we benchmark today.

I'm pretty sure clear is slow because we've never bothered to make it fast. It calls dispose() on every top level block in turn, and each block's dispose does stuff like resize the workspace's svg (because the contents have changed). There's also just a lot of DOM manipulation happening there.

Clear has nothing to do with XML serialization.

There's a lot of room to make that faster, and most of it looks pretty simple.

Clear has nothing to do with XML serialization.

Sorry @rachel-fenichel , I should have been more precise in my comment. I added a clarification above.

As per discussion today we are going to proceed with the "single workspace with saving / loading" model for now.

In the current implementation, we're avoiding serialization on switch (since the VM already has a representation of the workspace). This also means no extra serialized copy has to be stored in memory.

So, I think some of the action items here are:

  • Saving and reloading the undo/redo stack (most urgent since it actually effects functionality). Currently the stack gets lost when the workspace is cleared (i.e., when you switch sprites).
  • Making sure we handle toolbox changes in a sane way (variables, procedures, and different sets of blocks, e.g., for the stage).
  • Should we save/load pan position of the workspace per sprite? What about scale?
  • Optimizations of workspace.clear - do we need them, are they possible/easy?
  • Optimizations of Blockly.Xml.textToDom and Blockly.Xml.domToWorkspace - do we need them, are they possible/easy?

moving to backlog for additional questions

Was this page helpful?
0 / 5 - 0 ratings

Related issues

towerofnix picture towerofnix  路  6Comments

tmickel picture tmickel  路  6Comments

cwillisf picture cwillisf  路  4Comments

towerofnix picture towerofnix  路  4Comments

Alzter picture Alzter  路  3Comments