Single Line RichEdit Performance Runs

In applications that have lots of independent text instances such as spreadsheets and complex dialogs, one wants to know the tradeoffs between rapid display, memory usage, and editing functionality. As noted in the post Flyweight RichEdit Controls, using RichEdit to display such text avoids display glitches in switching from static display to edit mode and allows text to be copied, edited, and made accessible.On the other hand, simple text can be rendered considerably faster by calling system APIs. To compare the various approaches, I dusted off an old (circa 1996) program called grid that displays 300 windowless RichEdit controls and added a bunch of interesting options to check out run times for a variety of scenarios. This post relates some results. The text instances all consisted of single lines and it’ll be interesting to see how the relative times change for multiline instances as some point.

The following table lists run times in seconds for laying out and displaying 300 windowless RichEdit controls 10 times for various text types. For each display pass, the controls are loaded with text that identifies the display pass 0, … ,9, such as “1 Control 0”, for pass 1 and control 0. Including the load times significantly increases the math run times, since the math load time is larger than the corresponding math display time. Run times for simple text displayed by GDI’s ExtTextOut and DWrite’s IDWriteTextLayout are included for comparison. The computer used is a Hewlett Packard Z400 running Windows Server 2012 R2 Datacenter. RichEdit also has a fast GDI path (no LineServices) for simple text (no complex scripts), for which the simple case ran in 0.15 seconds. LineServices was active for RichEdit run times in the table. Two kinds of D2D/DWrite run times are listed: elegant typography and simple typography (ST). The latter only glyphs complex script runs and is significantly faster especially for single-line scenarios. It’s used by OneNote and Excel Universal.

Text

type

Ext

TextOut

IDWrite

TextLay

RE GDI

RE D2D

RE D2D

ST

RE GDI

PTS

RE D2D

PTS

RE D2D

PTS ST

Simple

0.03

0.09

0.35

0.44

0.17

0.43

0.66

0.38

BiDi

0.25

0.32

0.77

1.18

0.62

1.07

1.40

1.03

Ruby

1.32

1.77

1.12

1.61

2.03

1.45

Math

8.07

4.93

2.80

15.17

4.93

4.62

 

The simple text has strings like “Control 0”, the BiDi case has strings like “س Control 0”. The ruby object case is  meaning Japanese (nihongo) and the math text is the Pythagorean Theorem, entered via the linear format string a^2+b^2=c^2.

It’s striking that the D2D paths are significantly faster than the GDI paths for math zones. It will be interesting to examine these scenarios with profiling tools to see where we can improve the run times further. The math run times also depend on the format used for math strings. For the DWrite simple typography mode, the times are: RTF—2.46, linear format—2.80, MathML—3.34, and OMML—3.54.

When flyweight controls are used (one RichEdit control with 300 stories), the run times are about the same as with 300 single-story controls and about 30% less memory is used. This memory savings is reduced as the text size is increased, since the control overhead becomes a smaller fraction of the total memory usage.

Studying performance profiles reveals a couple of performance losses. With the RichEdit fast GDI path, there’s a call to GetObjectType to determine if the device context for rendering is an enhanced metafile. If this call is eliminated, the run time drops from 0.15 to 0.12. The difference becomes smaller the more lines one renders at a time.

The DWrite simple typography times are better than the enhanced typography times in large part because the LineServices line object cached when measuring a line is voided when the line is rendered and hence has to be recreated for rendering. This also becomes less important as more lines are rendered, since the line cache is voided whenever another line is measured and all line objects have to be recreated at render time.

From the run times in the table, it’s easy to conclude that using the OS APIs is considerably faster than using RichEdit for text that can be displayed by those APIs. When ruby or mathematical text is involved, the OS APIs are inadequate and RichEdit or some other more powerful rendering engine is needed. If the simple text needs to be edited, a RichEdit control can be instantiated for that instance. This is the approach used by Universal Excel. The challenge then arises to avoid glitches, e.g., font changes, going from the system function display to the RichEdit display. With general international text, avoiding all glitches can be quite tricky.