In the previous post we looked at the difference between using instances of master shapes versus using masterless shapes. Visio masters store shape information centrally, reducing memory and increasing performance. Shapes on the page inherit their properties from the master shape until the user makes a local change to override the inherited values. Today we want to analyze another aspect of inheritance and its impact on performance.
User-defined cells are a great way for shape designers and solution developers to add intelligence to a Visio shape. Unlike Scratch cells they support custom row names, making the Shapesheet more readable and understandable. The section name "User-defined" is a misnomer since these are designer / developer defined cells. They can be used to store information that does not need to be directly exposed to the users of the shape.
Suppose we have a solution that adds a new User-defined cell to store a timestamp recording when the shape was dropped. We might have some code that responds to the ShapeAdded event from Visio by inserting a User-defined section, adding a new named row and then setting the cell value. Alternatively, we could redesign our master shape to already have a User-defined section and the necessary named row in it. Our code only needs to set the cell value.
The first approach relies on working exclusively through the Visio API. The second approach relies on inheritance to provide the User-defined row. What are the performance implications here? We ran a little experiment programmatically dropping 100 shapes on the page. Each shape had 20 User-defined rows in each. In the first test, the User-defined rows were generated through code. The test took 688 milliseconds and the resulting memory usage was 41 kilobytes for the document. Compare that data to the inheritance scenario where the User-defined rows already existed and the code only needed to set cell values. This test took 360 milliseconds and the resulting memory usage was 36 kilobytes for the document.
The difference is that the inheritance scenario is almost twice as fast as the non-inheritance scenario. Most of this performance delta is caused by the additional work Visio must do to add new rows to the Shapesheet. Using inheritance lets Visio skip this step. Some of the performance delta is related to the increased memory consumption caused by having local copies of the User-defined rows in each shape. In the inheritance scenario, the User-defined rows are inherited but there are local values in the Value cell for each row. In the non-inheritance scenario, the rows themselves are also local.
There is an important issue to be aware of when working with User-defined rows - or any Shapesheet section with nameable rows. Adding a new row to the section breaks the inheritance of all existing rows in the section. The rows and cell values are forced to become local. This can adversely affect performance, memory consumption and file size because one addition can ruin the party for every other row. In our testing, this has added a 2% - 5% performance penalty to shapes with existing inherited User-defined rows. The penalty is caused by the increased memory requirements for the local row and cell data.
To summarize, programmatically adding new rows to the Shapesheet is a fairly expensive operation. It is far more efficient to pre-build the rows you need into your master shapes. Also adding a new row to a shape causes all the rows in that Shapesheet section to become local. This creates an ongoing performance impact because of the greater memory usage for the local data. Next time we'll look at the benefits of shallow nesting for group shapes as we continue to explore performance optimizations in Visio.