OpenEXR vs tinyexr
tinyexr is an excellent simple library for loading and saving OpenEXR files. It has one big advantage, in that it is very simple to start using: just one source file to compile and include! However, it also has some downsides, namely that not all features of OpenEXR are supported (for example, it can’t do PXR24, B44/B44A, DWAA/DWAB, HTJ2K compression modes), and performance might be behind the official library. It probably can’t do some of more exotic EXR features either (e.g. “deep” images), but I’ll ignore those for now.
But how large and how complex to use is the “official” OpenEXR library, anyways?
I do remember that a decade ago it was quite painful to build it, especially on anything that is not Linux. However these days (2025), that seems to be much simpler: it uses a CMake build system, and either directly vendors or automatically fetches whatever dependencies it needs, unless you really ask it to “please don’t do this”.
It is not exactly a “one source file” library though. However, I noticed that OpenUSD vendors OpenEXR “Core” library, builds it as a single C source file, and uses their own “nanoexr” wrapper around the API; see pxr/imaging/plugin/hioOpenEXR/OpenEXR. So I took that, adapted it to more recent OpenEXR versions (theirs uses 3.2.x, I updated to 3.4.4).
So I wrote a tiny app (github repo)
that reads an EXR file, and writes it back as downsampled EXR
(so this includes both reading & writing parts of an EXR library). And compared how large
is the binary size between tinyexr and OpenEXR, as well as their respective
source code sizes and performance.
Actual process was:
- Take OpenEXR source repository (v3.4.4, 2025 Nov),
- Take only the
src/lib/OpenEXRCoreandexternal/deflatefolders from it. openexr_config.h,compression.c,internal_ht.cpphave local changes! Look forLOCAL CHANGEcomments.
- Take only the
- Take OpenJPH source code, used 0.25.3 (2025 Nov),
put under
external/OpenJPH. - Take
openexr-c.c,openexr-c.h,OpenEXRCoreUnity.hfrom the OpenUSD repository. They were for OpenEXR v3.2, and needed some adaptations for later versions. OpenJPH part can’t be compiled as C, nor compiled as “single file”, so just include these source files into the build separately. - Take
tinyexrsource repository (v1.0.12, 2025 Mar).
Results
| Library | Binary size, KB | Source size, KB | read+write time, s | Notes |
|---|---|---|---|---|
| tinyexr 1.0.12 | 251 | 726 | 6.55 | |
| OpenEXR 3.2.4 | 2221 | 8556 | 2.19 | |
| OpenEXR 3.3.5 | 826 | 3831 | 1.68 | Removed giant DWAA/DWAB lookup tables. |
| OpenEXR 3.4.3 | 1149 | 5373 | 1.68 | Added HTJ2K compression (via OpenJPH). |
| OpenEXR 3.4.4 | 649 | 3216 | 1.65 | Removed more B44/DWA lookup tables. |
| + no HTJ2K | 370 | 1716 | Above, with HTJ2K/OpenJPH compiled out. | |
| + no DWA | 318 | Above, and with DWAA/DWAB compiled out. | ||
| + no B44 | 305 | Above, and with B44/B44A compiled out. | ||
| + no PXR24 | 303 | Above, and with PXR24 compiled out. |
Notes:
- Machine is Ryzen 5950X, Windows 10, compiler Visual Studio 2022 (17.14), Release build.
- This compares both tinyexr and OpenEXR in fully single-threaded mode. Tinyexr has threading capabilities, but it spins up and shuts down a whole thread pool for each processed image, which is a bit “meh”; and while OpenEXRCore can be threaded (and using full high level OpenEXR library does use it that way), the “nanoexr” wrapper I took from USD codebase does not do any threading.
- Timing is total time taken to read, downsample (by 2x) and write back 6 EXR files, input resolution 3840x2160, input files are ZIP FP16, ZIP FP32, ZIP w/ mips, ZIP tiled, PIZ and RLE compressed; output is ZIP compressed.
That’s it!