Skip to content

Memory-mapped reading ​

Readers that consume a whole file at once can memory-map it instead of copying it into a heap buffer. This is on by default for large files and needs no code change.

What it actually buys ​

Measured against one bulk read() into a pre-sized buffer — which is what these readers already did — mapping saves one full-file memcpy and the second copy of the file in RAM. It does not make the page faults go away.

So this is a memory-footprint feature more than a throughput one. On a multi-GB mesh it roughly halves peak RSS during the read. The clear exception is a reader that was building an ostringstream and calling .str() — that pays for two extra copies, and mapping removes both.

If you were expecting a large wall-clock win, this is not that feature; selective reads (see Selective reads) are where the read-time savings are.

When it engages ​

MmapModeBehaviour
Auto (default)map regular files ≥ 16 MiB
Onmap whenever possible
Offnever map

Mapping is advisory. A pipe, a zero-length file, a platform without support, WASM, or any failure in the mapping call itself falls back silently to a buffered read. It never turns a readable file into an error.

MESHIOPLUSPLUS_MMAP_THRESHOLD (bytes) overrides the Auto threshold, mainly for benchmarking; 0 maps everything.

One deliberate exception ​

A mapped buffer behaves as if NUL-terminated only because the kernel zero-fills the remainder of the final page — which the C string functions some parsers use (strtod) quietly rely on. That slack does not exist when the file size is an exact multiple of the page size, so those files are never mapped, even under On. It costs a buffered read roughly once in 4096 files and buys back a hard guarantee.

Coverage ​

Forty-five whole-file readers go through FileSource (v16.20.0 moved nineteen more over, on top of the original fifteen; the current text-I/O pass adds MFM, OFF, XYZ, DEX, IP, FLUX, PERMAS, Abaqus, Ansys coded databases, Nastran and Netgen): gmsh, tecplot, ansys_rst, xplt and gid's ASCII reader additionally honour an explicit ReadOptions::mMmap. The rest — vtk, vtu, ensight, ugrid's ASCII branch, openfoam, ansys (Fluent), femap, abaqus_fil, code_aster, pcd, marc, patran, radioss, radioss_anim, radioss_th, medit, ply, wkt, lsdyna, elmer, libmesh, mphtxt, z88, mfem, avsucd, su2, mdpa, lsdyna_binout, nastran_op2, unv, mfm, off, xyz, dex, ip, flux, permas, abaqus, ansysInp, nastran and netgen — read through it without an explicit mmap knob. Netgen's .vol.gz input views its inflated owned buffer; Marc retains each include source through deck parsing. OpenFOAM's ASCII lists, Ansys .cdb NBLOCK/EBLOCK, UNV nodes/elements/groups and GiD's HDF5 columns also reserve their containers from the counts they carry, capped by what the input could actually hold. The allocating tokenizers that remained have since moved onto the shared text cursor (benchmarks).

openfoam gained the most: it previously slurped via ostringstream + .str(), paying for two extra full-file copies on top of the read, both of which are now gone.

Streaming readers are unaffected by design — ugrid's binary branch reads each section straight into its destination array with no whole-file intermediate, so there is nothing to map, and the HDF5/netCDF-backed formats manage their own I/O.

Safety ​

Nothing in a returned Mesh ever points into the mapping: every reader copies what it parses into owning storage through the uniform mesh API. The mapping is therefore a function-local RAII object released when the reader returns — no keep-alive, no shared ownership, no capsule.

A test reads a file, then deletes and overwrites it, and re-validates the mesh, so a violation of that rule fails loudly in CI rather than becoming a use-after-free downstream.

Released under the MIT License.