PLYbench

October 22, 2024 · View on GitHub

This project provides a command-line application that benchmarks parsing and writing of PLY model data using different PLY parsing libraries. The benchmarks all populate a similar triangle mesh data structure. The benchmark tool uses Google Benchmark to ensure a proper benchmark approach is taken.

Introduction

PLYbench implements two sets of benchmarks, one set benchmarks parse performance of various PLY parsing libraries using various open source PLY models. The other set of benchmarks measures how various PLY libraries stack up against each other in terms of write performance, by writing a uniform triangle mesh with a fixed number of triangles.

These benchmarks were designed with the assumption that the target language in which the parser libraries are used is C++. Hence, the triangle mesh type that is used in the benchmark uses C++ types behind the scenes. It roughly has the following form:

#include <cstdint>
#include <vector>

struct Triangle
{
  std::int32_t a, b, c;
};

struct Vertex
{
  float x, y, z;
};

using Triangles = std::vector<Triangle>;
using Vertices = std::vector<Vertex>;

struct TriangleMesh
{
  Triangles triangles;
  Vertices vertices;
};

This puts some of the PLY libraries implemented in C at a slight disadvantage, since using those libraries incurs some additional overhead in the form of copying data from the C data structures into the mesh data structure. In practice, this is usually only a small part of the overall time required to parse a model.

Benchmark results

The following results were obtained on an AMD Ryzen 5 3600 6-Core processor using a Kingston A2000 NVMe SSD. PLYbench was compiled using GCC 13.21, with optimization level -O3. For each PLY library, an attempt was made to implement the most efficient way to either parser or write a PLY model. Some libraries support defining known list sizes for example to speed up parsing. It may be possible though that for some of the libraries, improvements are possible; pull requests are welcome!

For more information on the models that were used, see the Models section. For more information on the PLY libraries that were benchmarked, see the PLY libraries section.

Parse benchmark results

The following lists the average relative read performance of each PLY library averaged over all models that were used in the benchmarks, per PLY format type:

#Library nameOverallASCIIBinary big endianBinary little endian
1PLYwoot1.001.001.001.00
2miniply1.77x slower1.58x slower2.52x slower1.20x slower
3msh_ply3.18x slower5.79x slower2.46x slower1.29x slower
4nanoply6.82x slower12.43x slower3.70x slower4.34x slower
5tinyply 2.37.54x slowerN/A6.11x slower8.97x slower
6plylib9.89x slower11.90x slower7.26x slower10.50x slower
7RPly11.24x slower7.85x slower11.71x slower14.17x slower
8hapPLY18.79x slower28.90x slower12.31x slower15.15x slower

The following graph plots average CPU time per model per PLY library (lower numbers are better):

Parse CPU time

The following graph plots average transfer speeds in MiB per second for parsing per model per PLY library (higher numbers are better):

Parse transfer speed

Although these tests were run on a little endian machine, the transfer speeds for the models storing their data using a big endian representation achieved higher transfer speeds. This is due to the layout of the vertex data in the underlying PLY files. For the binary big endian models, parsing the data boils down to memcpy'ing large blocks of memory without having to stride over the data and after that do a big to little endian conversion. The binary little endian models require striding over the data in the PLY file, causing the slightly degraded performance.

Write benchmark results

The following lists the average relative write performance of each PLY library averaged per PLY format type, note that only binary little endian is tested as a binary output format in this case:

#Library nameOverallASCIIBinary
1PLYwoot1.001.001.00
2msh_ply4.16x slower7.29x slower1.04x slower
3nanoply5.46x slower9.16x slower1.76x slower
4RPly5.52x slower6.93x slower4.11x slower
5tinyply 2.35.58x slower7.87x slower3.30x slower
6hapPLY6.96x slower8.04x slower5.87x slower

The following graph plots average CPU time for writing a fixed triangle mesh with 100.000 semi-random triangles per PLY library (lower numbers are better):

Write CPU time

The following graph plots average transfer speeds in MiB per second for writing per PLY format type, per PLY library (higher numbers are better):

Write transfer speed

PLY libraries

The following PLY libraries are included in the benchmarks:

Library nameLanguageHeader-onlyAuthor
hapPLYC++Nicolas Sharp
miniplyC++Vilya Harvey
msh_plyCMaciej Halber
nanoplyC++vcglib
PLYwootC++Ton van den Heuvel
plylibC++vcglib
RPlyCDiego Nehab
tinyplyC++Dimitri Diakopoulos

Notes:

  1. At the time of writing, tinyply 2.3 seems to have issues reading ASCII files (https://github.com/ddiakopoulos/tinyply/issues/59). Benchmarking ASCII models was therefore disabled for tinyply.
  2. Miniply and plylib do not (directly) support writing PLY files, and are therefore excluded from the write benchmarks.
  3. nanoply does not correctly parse the 'PBRT-v3 Dragon' model, I suspect this is because it does not properly handle the face normals being defined as part of the face element.

Models

The following models are used in the benchmarks:

Model namePLY format type#Vertices#TrianglesSource
Stanford BunnyASCII3594769451Stanford 3D Scanning Repository
DragonASCII437645871414Stanford 3D Scanning Repository
Happy BuddhaASCII5436521087716Stanford 3D Scanning Repository
LucyBinary big endian1402787228055742Stanford 3D Scanning Repository
Asian DragonBinary big endian36096007219045Stanford 3D Scanning Repository
DOOM Combat SceneBinary little endian16128683224192Artec3D

The PLY models are not included in the repository. Use scripts/download_models.py to download the models used in the benchmarks and to be able to reproduce the benchmark results.

Reproducing the benchmark results

You can reproduce the benchmark results by building PLYbench from source. The following sections explain this in a bit more detail.

Dependencies

To be able to build PLYbench, the following dependencies are required:

Note that instead of Ninja a different build tool may be used (regular Make for example), but the configure script assumes Ninja is present on the system right now.

To be able to build the unit tests, the following dependency is required:

Note that to be able to reproduce the parse performance of PLYwoot for ASCII models, it is recommended to install the following dependencies as well:

To be able to use the script that downloads the PLY input models, Python 3 is required:

Finally, to be able to render the graphs, next to Python 3, Matplotlib is required:

Running the benchmarks

To be able to run the benchmarks on your own PC, you will first need to download the PLY models that are used as inputs for the various benchmarks. The PLY models are not included in the repository. The scripts/ directory contains a script to download the models though, use as follows:

$ scripts/download_models.py

This should download all models used in the benchmarks. The models will be stored in models/. Subsequently, after all required dependencies have been met, build PLYbench using CMake:

$ ./configure && ninja -C build -v

Finally, run the benchmarks as follows:

$ build/plybench --benchmark_out=benchmarks.json --benchmark_out_format=json

Generating the graphs

The generated JSON file benchmarks.json can be used as an input for scripts/plot_graph.py to render various graphs. For example, to generate the parse CPU times graph:

$ cat benchmarks.json | scripts/plot_graph.py -o parse_cpu_time.png -t parse_cpu_time

See scripts/plot_graph.py -h for more details.