rf-geom / ridgeforge Public

benchmarks

Numbers from one machine

Timings for version 0.9.4, measured on 2026-09-26, so that you can estimate whether an operation fits your budget before writing any code. They are not a comparison against anything, because we have not run anyone else's library carefully enough to publish the result.

Machine and method

Eight cores at 3.6 GHz, 32 GB of memory, Linux, GCC 13, -O2, asserts off. Each figure is the median of seven runs after one discarded warm-up. Wall time excludes reading and writing files, which are measured separately below. Peak memory is the high-water mark of resident size for the whole process, including the loaded mesh. Single thread unless a column says otherwise.

Test meshes

Five meshes, chosen because they fail in different ways rather than because they are pretty. None of them is distributed with the library; the shapes are described well enough to substitute your own.

NameTrianglesVerticesShape
terrain-tile4 010 0002 006 001heightfield, one open boundary, no sharp edges at all
scan-torso2 004 3201 003 112photogrammetry scan, 14 boundary loops, speckle components
statue-scan1 198 600599 302closed, genus 3, uniform triangle size
turbine-blade620 400310 202closed, genus 0, high curvature on two thin trailing edges
bracket-cad148 22074 112tessellated solid, genus 2, almost all vertices on an arris

Decimation at a glance

The same five test meshes, charted straight from bench.json. Switch the metric; the table under the next heading holds every figure for reading without the chart.

Decimation to 10 %

target_ratio = 0.1, boundaries preserved, ridges not locked except where noted.

MeshOut1 thread8 threadsPeak RSSCollapses/s
terrain-tile401 00012.1 s9.4 s392 MB192 k
scan-torso200 4325.9 s4.6 s192 MB196 k
statue-scan119 8603.4 s2.7 s118 MB199 k
turbine-blade62 0401.72 s1.35 s62 MB207 k
bracket-cad14 8220.39 s0.31 s16 MB215 k
bracket-cad, ridges locked31 4400.28 s0.23 s16 MB214 k

The last row is the interesting one. Locking sharp edges on a CAD part stops decimation at 21 % rather than the requested 10 %, and the run is shorter because it ends early - target_reached comes back false. Throughput per collapse is flat across all sizes, which is what you want from a heap-based algorithm and also means you can estimate a new mesh by multiplying.

Thread scaling

scan-torso, same work, varying threads:

ThreadsDecimateSpeed-upIntersection searchSpeed-up
15.9 s1.00×2.42 s1.00×
25.3 s1.11×1.27 s1.91×
44.8 s1.23×0.69 s3.51×
84.6 s1.28×0.51 s4.75×

Decimation barely scales, and that is structural rather than a defect to be fixed later: only the initial quadric accumulation is parallel, and it is about a quarter of the run. The collapse loop is sequential because each accepted collapse changes the costs of its neighbours, and any scheme that collapses two edges at once either locks or produces a different mesh. The intersection search, by contrast, is an embarrassingly parallel tree descent and behaves accordingly.

Other operations

All on scan-torso, single thread except where the table says otherwise.

OperationTimePeak RSSNote
Stats::of0.19 s52 MBone edge table, discarded on return
weld_vertices, tol 10⁻⁵0.44 s68 MBuniform grid of the tolerance cell size
remove_degenerate0.07 s38 MBone pass plus a compaction
orient_consistently0.26 s60 MBflood fill per component
fill_holes, 14 loops0.11 s38 MBlargest loop here is 214 edges
RidgeExtractor, 42°0.63 s74 MBemitted 1 840 polylines
Hierarchy build0.31 s60 MBmedian split, 8 triangles per leaf
SelfIntersection::find2.42 s72 MBfound 2 pairs; 0.51 s on 8 threads
Remesher, 5 iterations38 s148 MBwith projection; 23 s without

Remeshing is the expensive one by an order of magnitude, and most of that is the projection step rebuilding nothing but querying the hierarchy once per moved vertex per iteration. If a remesh is in your pipeline, budget for it as a batch job rather than as an interactive step.

File input and output

FormatFile sizeReadWriteRead rate
PLY, binary36.1 MB0.21 s0.15 s172 MB/s
PLY, ascii148 MB3.9 s2.8 s38 MB/s
STL, binary100 MB0.48 s0.37 s208 MB/s
OBJ, ascii131 MB3.6 s3.2 s36 MB/s

Ascii parsing is dominated by number conversion, which is why the two text formats land within ten percent of each other despite different syntax. Note the STL size: no vertex sharing means every triangle carries three full positions, so the file is three times the binary PLY while holding less information.

Predicted against measured memory

The per-element costs on the limits page are meant to be usable for planning. Checked against the measurements for scan-torso:

PhasePredictedMeasured peakDifference
Mesh resident only36 MB38 MB+5 %
Decimation184 MB192 MB+4 %
Intersection search68 MB72 MB+6 %

The excess is the allocator's own bookkeeping and the growth slack in vectors that were not reserved exactly. Four to six percent is close enough that the arithmetic is worth doing before you discover a limit on a build machine.

What these numbers do not tell you