HexaPDF 0.14.1 - Performance Work Published on

This smaller release mostly contains performance enhancements. Due to them being especially benefical in long running processes that handle a lot of PDFs, updating is still very recommended.

The following script was used to evaluate TrueType font and serialization related optimizations:

require 'hexapdf'

font_path = '/usr/share/fonts/truetype/liberation/LiberationSans-BoldItalic.ttf'
font_file = HexaPDF::Font::TrueType::Font.new(File.open(font_path, 'rb'))

1000.times do
  doc = HexaPDF::Document.new
  font = doc.fonts.add(font_path)
  doc.pages.add.canvas.
    font(font, size: 10).
    text("The quick brown fox jumps over the lazy dog", at: [200, 200])
  doc.write('/tmp/out.pdf')
end

In HexaPDF 0.14.0 this script allocated about 2.74 million objects and took 4.20 seconds to run. With this release object allocation was reduced by around 33% to 1.85 million and runtime by around 23% to 3.21 seconds.

These benefits are without any changes to existing code. By using the new font loader to load the TrueType font object only once (e.g. in the code above use font_file instead of font_path), we can further reduce the object allocation to 1.29 million and runtime to 2.41 seconds.

Furthermore, the HexaPDF::Importer class has been modified to avoid problems with memory retention. If you have had problems with that, you should definitely try out this new version!

Here is a before and after comparison for the “raw_text” benchmark:

|--------------------------------------------------------------------|
|                              ||    Time |     Memory |   File size |
|--------------------------------------------------------------------|
| hexapdf     | 1x             |    558ms |  34.280KiB |     452.602 |
|--------------------------------------------------------------------|
| hexapdf     | 5x             |  1.859ms |  45.296KiB |   2.258.904 |
|--------------------------------------------------------------------|
| hexapdf     | 10x            |  3.553ms |  57.376KiB |   4.517.825 |
|--------------------------------------------------------------------|
| hexapdf     | 1x ttf         |    588ms |  33.288KiB |     549.526 |
|--------------------------------------------------------------------|
| hexapdf     | 5x ttf         |  2.305ms |  48.916KiB |   2.687.121 |
|--------------------------------------------------------------------|
| hexapdf     | 10x ttf        |  4.492ms |  63.636KiB |   5.360.945 |
|--------------------------------------------------------------------|

vs.

|--------------------------------------------------------------------|
|                              ||    Time |     Memory |   File size |
|--------------------------------------------------------------------|
| hexapdf     | 1x             |    562ms |  34.556KiB |     452.598 |
|--------------------------------------------------------------------|
| hexapdf     | 5x             |  1.883ms |  45.268KiB |   2.258.904 |
|--------------------------------------------------------------------|
| hexapdf     | 10x            |  3.634ms |  56.628KiB |   4.517.827 |
|--------------------------------------------------------------------|
| hexapdf     | 1x ttf         |    557ms |  33.392KiB |     546.390 |
|--------------------------------------------------------------------|
| hexapdf     | 5x ttf         |  2.113ms |  43.408KiB |   2.670.953 |
|--------------------------------------------------------------------|
| hexapdf     | 10x ttf        |  4.174ms |  63.360KiB |   5.328.382 |
|--------------------------------------------------------------------|

And here the one for the “line_wrapping” benchmark:

|--------------------------------------------------------------------|
|                              ||    Time |     Memory |   File size |
|--------------------------------------------------------------------|
| hexapdf L | 400              |  1.237ms | 101.112KiB |     361.690 |
| hexapdf C | 400              |  1.387ms | 109.072KiB |     361.692 |
|--------------------------------------------------------------------|
| hexapdf L | 200              |  1.353ms |  94.572KiB |     408.710 |
| hexapdf C | 200              |  1.565ms | 100.992KiB |     408.708 |
|--------------------------------------------------------------------|
| hexapdf L | 100              |  1.541ms |  89.644KiB |     464.256 |
| hexapdf C | 100              |  1.847ms |  97.432KiB |     464.256 |
|--------------------------------------------------------------------|
| hexapdf L | 50               |  2.482ms | 200.512KiB |     569.798 |
| hexapdf C | 50               |  3.004ms | 220.296KiB |     569.807 |
|--------------------------------------------------------------------|
| hexapdf L | 400 ttf          |  1.305ms |  89.352KiB |     445.395 |
| hexapdf C | 400 ttf          |  1.468ms | 108.136KiB |     445.418 |
|--------------------------------------------------------------------|
| hexapdf L | 200 ttf          |  1.481ms |  84.948KiB |     508.007 |
| hexapdf C | 200 ttf          |  1.701ms |  99.004KiB |     508.035 |
|--------------------------------------------------------------------|
| hexapdf L | 100 ttf          |  1.817ms |  91.772KiB |     611.460 |
| hexapdf C | 100 ttf          |  2.163ms |  96.568KiB |     611.481 |
|--------------------------------------------------------------------|
| hexapdf L | 50 ttf           |  4.547ms | 260.596KiB |     772.065 |
| hexapdf C | 50 ttf           |  5.392ms | 281.268KiB |     772.078 |
|--------------------------------------------------------------------|

vs.

|--------------------------------------------------------------------|
|                              ||    Time |     Memory |   File size |
|--------------------------------------------------------------------|
| hexapdf L | 400              |  1.274ms |  98.812KiB |     361.689 |
| hexapdf C | 400              |  1.397ms | 108.092KiB |     361.689 |
|--------------------------------------------------------------------|
| hexapdf L | 200              |  1.349ms |  91.496KiB |     408.708 |
| hexapdf C | 200              |  1.588ms | 100.076KiB |     408.708 |
|--------------------------------------------------------------------|
| hexapdf L | 100              |  1.520ms |  88.728KiB |     464.257 |
| hexapdf C | 100              |  1.886ms |  96.052KiB |     464.255 |
|--------------------------------------------------------------------|
| hexapdf L | 50               |  2.563ms | 207.776KiB |     569.797 |
| hexapdf C | 50               |  2.981ms | 221.416KiB |     569.807 |
|--------------------------------------------------------------------|
| hexapdf L | 400 ttf          |  1.317ms | 104.032KiB |     442.909 |
| hexapdf C | 400 ttf          |  1.485ms | 104.380KiB |     442.931 |
|--------------------------------------------------------------------|
| hexapdf L | 200 ttf          |  1.439ms |  97.984KiB |     505.202 |
| hexapdf C | 200 ttf          |  1.652ms |  95.064KiB |     505.224 |
|--------------------------------------------------------------------|
| hexapdf L | 100 ttf          |  1.766ms |  94.324KiB |     607.750 |
| hexapdf C | 100 ttf          |  2.105ms |  92.756KiB |     607.768 |
|--------------------------------------------------------------------|
| hexapdf L | 50 ttf           |  4.413ms | 282.288KiB |     769.931 |
| hexapdf C | 50 ttf           |  5.272ms | 283.920KiB |     769.952 |
|--------------------------------------------------------------------|

The larger TrueType benchmarks are all between 3% and 7% faster than before.

As always, have a look at the changelog for an overview of all changes.