Instead of wrapping the tree returned by libcmark, we traverse the tree to create a pure ruby structure, to avoid memory management hassles and make the AST easier to manipulate. And instead of using libcmark’s fast HTML renderer, we provide a pure ruby HTML renderer which can easily be customized and extended. We also provide idiomatic ruby functions for manipulating the AST.
I’m not sure about this approach – it might be better, performance-wise, to wrap the nodes returned by libcmark, and I might switch to that approach at some point.
I ran the same benchmark on it that I use for cmark. It parsed and rendered a 10 MB Markdown file in 2.7 seconds on an old (Intel Core 2 Due 2GHz) machine. That is about the same speed as commonmark.js, and about seven times faster than kramdown.
If that’s not fast enough, you can use the simple wrapper.rb in the CommonMark repository, which completes the same benchmark in 0.35 seconds.
I think it should be possible to optimize commonmarker considerably to close much of the gap between it and the simple wrapper.
I’ve changed the library so that the Node structure wraps pointers to structs allocated by libcmark, instead of creating a regular ruby array. This vastly improves performance when the native C HTML renderer is used. New benchmarks:
input size = 10031600 bytes
commonmarker with HtmlNativeRenderer 0.188 s
commonmarker with ruby HtmlRenderer 2.418 s
redcarpet 0.127 s
kramdown 14.700 s