Those of you who follow me on Twitter already know that I was using MGTemplateEngine for generating appledoc html files. I tweaked it a bit to make it work for me – the main point was to add ability of function-like sections that can be called with arbitrary parameters.
It worked well and I think it’s really great library, however it did emit some parts of my sections which puzzled me and I couldn’t find a reason. I was also a bit concerned about performance – it took approximately 1.6 seconds to generate output for a single file. Of course this included all the overhead of reading the template file and parsing sections out of it which was only necessary doing once and then reusing it for all templates. But still, the generating html files for the whole appledoc documentation took around 40 seconds.
Even though I have some optimizations in mind like caching times of last changes, the issue of unexpected output still remained. I’m not saying the engine isn’t working properly – far from it, it’s fine engine and “hat down” to Matt for giving it for free! It’s also used in many apps out there. But not every library is suited for every use and when I find myself tweaking the internals too often, it’s probably time to rethink my strategy.
So I went searching for possible replacement. Googling for “MGTemplateEngine alternative” did yield some links, but I wasn’t getting anywhere. If nothing else, I could use parsekit for this task as well; it’s already used for source code parsing anyway. This would certainly be doable, but seemed like a lot of work. So back to Google I went for take 2. This time decided to go with more general search terms and after a while came arround GRMustache. Now this seemed interesting so I cloned the repository and copied over library files to a test project. The first thing I noticed it worked with garbage collection on out of the box (MGTemplateEngine did require some tweaking to make it work, although most of it came to RegexKitLite). Anyway, there were few clang 2.0 warnings about parenthesis, but that was solved in a minute or so.
Reading the online documentation immediately caught my attention – it seemed to support “functions” out of the box. It also stated that these can be recursive, which is essential for certain parts in appledoc. After half an hour or so, it became apparent that this library could be a way to go. So I created a new git branch, cleaned up the project of MGTemplateEngine and related classes and started working with GRMustache. It took me around a day of work to get back the same functionality. I first started with the code that relied on the implementation and unit tests. This took most of the time. Then it was time to work on template file itself. I could copy/paste most of the old file, but did require some additions to code too as GRMustache doesn’t support testing for different values for example. In most cases I needed to add BOOL properties that tested simple stuff like “is the number of objects greater than zero”. Not a big deal in my mind, especially given the fact that the output was predictable and no hacking was needed.
Ok, how about performance? appledoc already includes timing for major tasks which is written to the standard output, so I used that in both cases. Here’s how it dealt with a single file with MGTemplateEngine:
Finished in 1744ms. Parsing: 21ms (1%) Processing: 35ms (2%) Generating: 1688ms (96%)
And the same file with GRMustache?
Finished in 141ms. Parsing: 22ms (15%) Processing: 16ms (11%) Generating: 103ms (73%)
Still most of the time was spent in generating output, but noticeably faster. How about the whole appledoc project? MGTemplateEngine:
Finished in 40583ms. Parsing: 1426ms (3%) Processing: 523ms (1%) Generating: 38634ms (95%)
Finished in 15157ms. Parsing: 3583ms (23%) Processing: 2319ms (15%) Generating: 9255ms (61%)
Great! Now do bear in mind that I’m using compiled script with GRMustache. Didn’t even test without it, so can’t tell the difference it makes. I might update the post in the future with these tests too. But on the other hand, it doesn’t really matter to me – if it’s working noticeably faster without any additional work from my side, than I’m happy and so will be all appledoc users.
If you’re in need of a template engine do give GRMustache a try. It’s well implemented, has very readable code and is fast. In comparison with MGTemplateEngine it does require slightly more work in code, while MGTemplateEngine allows such testing within the template itself. But in most cases this is completely trivial stuff, like adding BOOL properties for testing if a list is empty for example. On the other hand it does force the separation between logic and layout, so it’s not necessarily bad thing. Just be aware of it. In most cases you’d want to create an intermediate object (NSDictionary, NSArray or similar) that is suited for the template anyway, but in appledoc case I didn’t want to go that way as I already had in-memory representation of the parsed data prepared specifically for generating output.
Another thing I noticed was the template file looks much more readable with GRMustache that it was with MGTemplateEngine. Take a look for yourself: MGTemplateEngine template vs. GRMustache template (download the files and open them in text editor to really view the difference!). Here’s also the link to generated documentation.
It was real fun to come back to appledoc after more than a month. It will probably take me some more time before I can give it more attention, but now when one of the major tasks is almost complete, I can do short iterations and add additional functionality such as index and hierarchy files. And then on to doc set and we’re done!
Gwendal Roué, the author of GRMustache notified me about his update to v1.1.3, which includes performance improvements with his own benchmarks showing template files parsing gain of 30% and output generation of 70%. I included the new version to appledoc and run the same tests as before. This time, generation html files for the whole project consistently took drastically less time than before:
Finished in 4956ms. Parsing: 1479ms (29%) Processing: 584ms (11%) Generating: 2893ms (58%)
Now that’s 8x faster than MGTemplateEngine and 3x faster than GRMustache 1.1.0! This didn’t make sense, so I switched back to GRMustache 1.1.0 and ran the same test again. This time I got consistent times of around 5500ms. So there was obviously something wrong with my last time benchmarks. Looking at the numbers more closely I noticed parsing and processing times were way longer when doing benchmarks for GRMustache 1.1.0 (3500ms/2300ms vs something in the range of 1500ms/500ms with all other tests). So I obviously measured performance while some background processor/hard disk intensive process was running and I didn’t notice it… Mea culpa, no doubt about it :) With new benchmarks, the performance gain by upgrading to GRMustache v1.1.3 is around 12%. As appledoc generation phase is practically 100% of template engine, this would be consistent with Gwendal’s results - as this is also the phase where all output files are written, most of the time is actually spent due to hitting the hard disk.
All in all, using GRMustache turns out to be several times faster, and that’s very impressive by itself. Coupled with other advantages, described in this post, it’s win/win solution for appledoc!