mirror of https://github.com/CGAL/cgal
Add benchmarks
This commit is contained in:
parent
998f5f3f68
commit
d88bc68ba3
|
|
@ -0,0 +1,15 @@
|
|||
# Created by the script cgal_create_cmake_script
|
||||
# This is the CMake script for compiling a CGAL application.
|
||||
|
||||
cmake_minimum_required(VERSION 3.1...3.14)
|
||||
project( Isosurfacing_3_benchmark )
|
||||
|
||||
find_package(CGAL REQUIRED)
|
||||
|
||||
create_single_source_cgal_program( "benchmark.cpp" )
|
||||
|
||||
find_package(TBB)
|
||||
include(CGAL_TBB_support)
|
||||
if(TARGET CGAL::TBB_support)
|
||||
target_link_libraries(benchmark PUBLIC CGAL::TBB_support)
|
||||
endif()
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef CGAL_TIMER_H
|
||||
#define CGAL_TIMER_H
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
class ScopeTimer {
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
|
||||
public:
|
||||
ScopeTimer() : start(Clock::now()), msg("Duration"), running(true) {}
|
||||
|
||||
explicit ScopeTimer(const std::string& msg) : start(Clock::now()), msg(msg), running(true) {
|
||||
std::cout << msg << "..." << std::endl;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
start = Clock::now();
|
||||
running = true;
|
||||
}
|
||||
|
||||
int64_t stop() {
|
||||
TimePoint end = Clock::now();
|
||||
running = false;
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
}
|
||||
|
||||
~ScopeTimer() {
|
||||
if (running) {
|
||||
TimePoint end = Clock::now();
|
||||
int64_t duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
std::cout << msg << ": " << duration << " ms" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TimePoint start;
|
||||
std::string msg;
|
||||
bool running;
|
||||
};
|
||||
|
||||
#endif // CGAL_TIMER_H
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
#include <CGAL/Cartesian.h>
|
||||
#include <CGAL/Cartesian_grid_3.h>
|
||||
#include <CGAL/Cartesian_grid_domain.h>
|
||||
#include <CGAL/Dual_contouring_3.h>
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Implicit_domain.h>
|
||||
#include <CGAL/Marching_cubes_3.h>
|
||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/boost/graph/IO/OFF.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#include "CLI11.hpp"
|
||||
#include "Timer.h"
|
||||
|
||||
template <class GeomTraits>
|
||||
struct SphereValue {
|
||||
typename GeomTraits::FT operator()(const typename GeomTraits::Point_3& point) const {
|
||||
return CGAL::approximate_sqrt((point - CGAL::ORIGIN).squared_length());
|
||||
}
|
||||
};
|
||||
|
||||
template <class GeomTraits>
|
||||
struct SphereGradient {
|
||||
typename GeomTraits::Vector_3 operator()(const typename GeomTraits::Point_3& point) const {
|
||||
const typename GeomTraits::Vector_3 g = point - CGAL::ORIGIN;
|
||||
return g / CGAL::approximate_sqrt(g.squared_length());
|
||||
}
|
||||
};
|
||||
|
||||
template <class GeomTraits>
|
||||
struct Implicit_sphere {
|
||||
|
||||
typedef CGAL::Isosurfacing::Implicit_domain<GeomTraits, SphereValue<GeomTraits>, SphereGradient<GeomTraits>> Domain;
|
||||
typedef typename GeomTraits::Vector_3 Vector;
|
||||
|
||||
Domain domain(const std::size_t N) const {
|
||||
|
||||
const Vector res(2.0 / N, 2.0 / N, 2.0 / N);
|
||||
return Domain({-1, -1, -1, 1, 1, 1}, res, SphereValue<GeomTraits>(), SphereGradient<GeomTraits>());
|
||||
}
|
||||
|
||||
typename GeomTraits::FT iso() const {
|
||||
return 0.8;
|
||||
}
|
||||
};
|
||||
|
||||
template <class GeomTraits>
|
||||
struct Grid_sphere {
|
||||
|
||||
typedef CGAL::Isosurfacing::Cartesian_grid_domain<GeomTraits> Domain;
|
||||
typedef CGAL::Cartesian_grid_3<GeomTraits> Grid;
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Point_3 Point;
|
||||
|
||||
Domain domain(const std::size_t N) const {
|
||||
|
||||
const FT resolution = 2.0 / N;
|
||||
SphereValue<GeomTraits> val;
|
||||
SphereGradient<GeomTraits> grad;
|
||||
|
||||
Grid grid(N, N, N, {-1, -1, -1, 1, 1, 1});
|
||||
|
||||
for (std::size_t x = 0; x < grid.xdim(); x++) {
|
||||
const FT xp = x * resolution - 1.0;
|
||||
|
||||
for (std::size_t y = 0; y < grid.ydim(); y++) {
|
||||
const FT yp = y * resolution - 1.0;
|
||||
|
||||
for (std::size_t z = 0; z < grid.zdim(); z++) {
|
||||
const FT zp = z * resolution - 1.0;
|
||||
|
||||
grid.value(x, y, z) = val(Point(xp, yp, zp));
|
||||
grid.gradient(x, y, z) = grad(Point(xp, yp, zp));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Domain(grid);
|
||||
}
|
||||
|
||||
typename GeomTraits::FT iso() const {
|
||||
return 0.8;
|
||||
}
|
||||
};
|
||||
|
||||
template <class GeomTraits>
|
||||
struct Skull_image {
|
||||
|
||||
typedef CGAL::Isosurfacing::Cartesian_grid_domain<GeomTraits> Domain;
|
||||
typedef CGAL::Cartesian_grid_3<GeomTraits> Grid;
|
||||
|
||||
Domain domain(const std::size_t N) const {
|
||||
|
||||
const std::string fname = "../data/skull_2.9.inr";
|
||||
CGAL::Image_3 image;
|
||||
if (!image.read(fname)) {
|
||||
std::cerr << "Error: Cannot read file " << fname << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Grid grid(image);
|
||||
return Domain(grid);
|
||||
}
|
||||
|
||||
typename GeomTraits::FT iso() const {
|
||||
return 2.9;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
CLI::App app{"Isosurfacing benchmarks"};
|
||||
|
||||
std::size_t N = 2;
|
||||
app.add_option("-N", N, "Grid size");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
#if defined KERNEL_SIMPLE_CARTESIAN_DOUBLE
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
#elif defined KERNEL_SIMPLE_CARTESIAN_FLOAT
|
||||
typedef CGAL::Simple_cartesian<float> Kernel;
|
||||
#elif defined KERNEL_CARTESIAN_DOUBLE
|
||||
typedef CGAL::Cartesian<double> Kernel;
|
||||
#elif defined KERNEL_EPIC
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
|
||||
#else
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
#endif
|
||||
|
||||
typedef Kernel::Point_3 Point;
|
||||
|
||||
typedef std::vector<Point> Point_range;
|
||||
typedef std::vector<std::vector<std::size_t>> Polygon_range;
|
||||
|
||||
#if defined SCENARIO_GRID_SPHERE
|
||||
auto scenario = Grid_sphere<Kernel>();
|
||||
#elif defined SCENARIO_IMPLICIT_SPHERE
|
||||
auto scenario = Implicit_sphere<Kernel>();
|
||||
#elif defined SCENARIO_SKULL_IMAGE
|
||||
auto scenario = Skull_image<Kernel>();
|
||||
#else
|
||||
auto scenario = Implicit_sphere<Kernel>();
|
||||
#endif
|
||||
|
||||
Point_range points;
|
||||
Polygon_range polygons;
|
||||
|
||||
ScopeTimer timer;
|
||||
|
||||
#if defined ALGO_MARCHING_CUBES
|
||||
CGAL::Isosurfacing::make_triangle_mesh_using_marching_cubes(scenario.domain(N), scenario.iso(), points, polygons);
|
||||
#elif defined ALGO_DUAL_CONTOURING
|
||||
CGAL::Isosurfacing::make_quad_mesh_using_dual_contouring(scenario.domain(N), scenario.iso(), points, polygons);
|
||||
#else
|
||||
CGAL::Isosurfacing::make_triangle_mesh_using_marching_cubes(scenario.domain(N), scenario.iso(), points, polygons);
|
||||
#endif
|
||||
|
||||
const int64_t ms = timer.stop();
|
||||
|
||||
if (points.size() > std::numeric_limits<std::size_t>::max() - 2) {
|
||||
std::cout << "This should never print and only prevents optimizations" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << ms << std::endl;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from benchmark_util import *
|
||||
|
||||
scenario = "SCENARIO_GRID_SPHERE"
|
||||
kernel = "KERNEL_SIMPLE_CARTESIAN_FLOAT"
|
||||
algorithm = "ALGO_DUAL_CONTOURING"
|
||||
threads = 1
|
||||
exponent = 2
|
||||
min_cells = 10
|
||||
max_cells = 1000
|
||||
|
||||
build(scenario, kernel, algorithm)
|
||||
|
||||
data = []
|
||||
|
||||
c = min_cells
|
||||
while c < max_cells:
|
||||
n = int(c ** (1.0 / 3.0))
|
||||
|
||||
time = execute(n)
|
||||
data.append([scenario, kernel, algorithm, threads, c, time])
|
||||
|
||||
c *= exponent
|
||||
|
||||
df = pd.DataFrame(data, comulmns=["scenario", "kernel", "algorithm", "threads", "cells", "time"])
|
||||
|
||||
df.to_csv("benchmark_size.csv")
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
from benchmark_util import *
|
||||
|
||||
scenario = "SCENARIO_GRID_SPHERE"
|
||||
kernel = "KERNEL_SIMPLE_CARTESIAN_FLOAT"
|
||||
algorithm = "ALGO_DUAL_CONTOURING"
|
||||
min_threads = 1
|
||||
max_threads = 8
|
||||
cells = 1000000
|
||||
|
||||
build(scenario, kernel, algorithm)
|
||||
|
||||
data = []
|
||||
|
||||
for t in range(min_threads, max_threads):
|
||||
time = execute(cells)
|
||||
|
||||
data.append([scenario, kernel, algorithm, t, cells, 0])
|
||||
|
||||
df = pd.DataFrame(data, comulmns=["scenario", "kernel", "algorithm", "threads", "cells", "time"])
|
||||
|
||||
df.to_csv("benchmark_threads.csv")
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import pandas as pd
|
||||
|
||||
def print_stream(stream):
|
||||
while True:
|
||||
line = stream.readline()
|
||||
if not line:
|
||||
break
|
||||
print(line, end="")
|
||||
|
||||
def run(cmd, output=True):
|
||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
|
||||
exit_code = process.wait()
|
||||
if output:
|
||||
print_stream(process.stdout)
|
||||
if exit_code != 0:
|
||||
print_stream(process.stderr)
|
||||
sys.exit(exit_code)
|
||||
return process
|
||||
|
||||
def build(scenario, kernel, algorithm):
|
||||
run(["cmake", "-E", "make_directory", "build"])
|
||||
run(["cmake", "-B", "build", "-DCMAKE_BUILD_TYPE=Release", "-DCGAL_DIR=../../../", "-D" + scenario + "= -D" + kernel + "= -D" + algorithm + "="])
|
||||
run(["cmake", "--build", "build"])
|
||||
|
||||
def execute(n, times=1):
|
||||
time = 0
|
||||
for i in range(times):
|
||||
run(["./build/benchmark", "-N", str(n)], False)
|
||||
time += int(process.stdout.readline())
|
||||
return time / times
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def save_svg(file):
|
||||
fig = plt.gcf()
|
||||
fig.set_size_inches((10, 5), forward=False)
|
||||
plt.savefig(file, bbox_inches="tight")
|
||||
|
||||
|
||||
def add_threads_graph(data, label):
|
||||
x = data["threads"]
|
||||
y = data["cells"] / data["time"] / 10 ** 3
|
||||
plt.plot(x, y, label=label)
|
||||
plt.legend()
|
||||
|
||||
|
||||
def add_size_graph(data, label):
|
||||
x = data["cells"]
|
||||
y = data["cells"] / data["time"] / 10 ** 3
|
||||
plt.plot(x, y, label=label)
|
||||
plt.legend()
|
||||
|
||||
|
||||
def plot_graph(file, name, log, ylabel, xlabel):
|
||||
plt.title(name)
|
||||
plt.xlabel(xlabel)
|
||||
plt.ylabel(ylabel)
|
||||
if log:
|
||||
plt.xscale("log")
|
||||
plt.gca().yaxis.grid(color='#cccccc')
|
||||
plt.gca().xaxis.grid(color='#cccccc')
|
||||
plt.ylim(ymin=0)
|
||||
save_svg(file)
|
||||
plt.show()
|
||||
|
||||
|
||||
latex_export = True
|
||||
if latex_export:
|
||||
#plt.rcParams["svg.fonttype"] = "none"
|
||||
plt.rcParams["axes.unicode_minus"] = False
|
||||
plt.rcParams['font.size'] = "17"
|
||||
|
||||
|
||||
data = pd.read_csv("threads_implicit.csv", sep=";")
|
||||
add_threads_graph(data, "implicit")
|
||||
|
||||
data = pd.read_csv("threads_grid.csv", sep=";")
|
||||
add_threads_graph(data, "grid")
|
||||
|
||||
plt.xticks(np.arange(1, max(data["threads"]) + 0.1, 1))
|
||||
plot_graph("perf_threads.svg", "", False, "performance [10^3 cubes/s]", "cores")
|
||||
|
||||
|
||||
data = pd.read_csv("size_implicit.csv", sep=";")
|
||||
add_size_graph(data, "implicit")
|
||||
|
||||
data = pd.read_csv("size_grid.csv", sep=";")
|
||||
add_size_graph(data, "grid")
|
||||
|
||||
plot_graph("perf_size.svg", "", True, "performance [10^3 cubes/s]", "cubes")
|
||||
Loading…
Reference in New Issue