Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ cmake_minimum_required(VERSION 3.10)
#-----------------------------------------------------------------------------
# Options
#-----------------------------------------------------------------------------
option(BOOST_GIL_BUILD_BENCHMARKS "Builds all available benchmarks in the benchmark directory" OFF)
option(BOOST_GIL_BUILD_EXAMPLES "Build examples" ON)
option(BOOST_GIL_BUILD_HEADER_TESTS "Enable self-contained header tests" ON)
option(BOOST_GIL_ENABLE_EXT_DYNAMIC_IMAGE "Enable Dynamic Image extension, tests and examples" ON)
Expand Down Expand Up @@ -157,7 +158,7 @@ set(Boost_required_components headers)
if (BOOST_GIL_USE_BOOST_FILESYSTEM)
list(APPEND Boost_required_components filesystem)
endif()
find_package(Boost 1.80.0 REQUIRED COMPONENTS ${Boost_required_components})
find_package(Boost CONFIG 1.80.0 REQUIRED COMPONENTS ${Boost_required_components})
message(STATUS "Boost.GIL: Using Boost_INCLUDE_DIRS=${Boost_INCLUDE_DIRS}")
message(STATUS "Boost.GIL: Using Boost_LIBRARY_DIRS=${Boost_LIBRARY_DIRS}")

Expand Down Expand Up @@ -307,4 +308,11 @@ if(BOOST_GIL_BUILD_EXAMPLES AND BOOST_GIL_ENABLE_EXT_IO)
add_subdirectory(example)
endif()

#-----------------------------------------------------------------------------
# Benchmarks
#-----------------------------------------------------------------------------
if(BOOST_GIL_BUILD_BENCHMARKS)
add_subdirectory(benchmark)
endif()

endif()
85 changes: 85 additions & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
message(STATUS "Boost.GIL: Configuring benchmarks")

#-----------------------------------------------------------------------------
# Deploy google/benchmark
#-----------------------------------------------------------------------------
include(ExternalProject)

# NOTE for Ninja generator:
# Ninja generator will try to find external project library at the expected location.
# BUILD_BYPRODUCTS option is added to specify build output and satisfy dependencies.
ExternalProject_Add(project_googlebenchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG "v1.9.5"
GIT_SHALLOW 1
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/vendor/benchmark
TIMEOUT 10
BUILD_BYPRODUCTS <INSTALL_DIR>/lib/${CMAKE_STATIC_LIBRARY_PREFIX}benchmark${CMAKE_STATIC_LIBRARY_SUFFIX}
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/vendor/benchmark
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_LIBDIR=lib
-DBENCHMARK_ENABLE_TESTING=OFF
-DBENCHMARK_ENABLE_GTEST_TESTS=OFF
-DBENCHMARK_ENABLE_WERROR=OFF
UPDATE_COMMAND ""
TEST_COMMAND "")

ExternalProject_Get_Property(project_googlebenchmark INSTALL_DIR)

file(MAKE_DIRECTORY ${INSTALL_DIR}/include)
add_library(gil_googlebenchmark STATIC IMPORTED)
target_include_directories(gil_googlebenchmark INTERFACE ${INSTALL_DIR}/include)
target_compile_definitions(gil_googlebenchmark INTERFACE BENCHMARK_STATIC_DEFINE)
set_property(TARGET gil_googlebenchmark PROPERTY IMPORTED_LOCATION
"${INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}benchmark${CMAKE_STATIC_LIBRARY_SUFFIX}")

if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
find_library(SHLWAPI Shlwapi.lib)
target_link_libraries(gil_googlebenchmark INTERFACE SHLWAPI)
else()
find_package(Threads)
target_link_libraries(gil_googlebenchmark INTERFACE ${CMAKE_THREAD_LIBS_INIT} rt)
endif()

add_dependencies(gil_googlebenchmark project_googlebenchmark)

#-----------------------------------------------------------------------------
# Build benchmarks
#-----------------------------------------------------------------------------
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
file(GLOB _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND)
else()
file(GLOB _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
endif()

foreach(_benchmark ${_benchmarks})
get_filename_component(_name ${_benchmark} NAME_WE)
set(_target benchmark_${_name})
add_executable(${_target} ${_name}.cpp)
target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1)
target_link_libraries(${_target}
PRIVATE
gil_compile_options
gil_include_directories
gil_dependencies
gil_googlebenchmark)
endforeach()

unset(_benchmarks)
unset(_benchmark)


#-----------------------------------------------------------------------------
# Options
#-----------------------------------------------------------------------------
option(BOOST_GIL_BUILD_BENCHMARKS_OPENCV "Builds all OpenCV benchmarks" OFF)
option(BOOST_GIL_BUILD_BENCHMARKS_IPP "Builds all IPP benchmarks" OFF)

if(BOOST_GIL_BUILD_BENCHMARKS_OPENCV)
add_subdirectory(opencv)
endif()

if(BOOST_GIL_BUILD_BENCHMARKS_IPP)
add_subdirectory(ipp)
endif()
68 changes: 68 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# GIL Benchmarks

This guide assumes that minimal GIL can already be built, e.g. dependency on
dependent Boost libraries are satisfied.

## Dependencies

The benchmarks depend on the popular micro-benchmarking framework:

* https://github.com/google/benchmark

It is configured using
[ExternalProject_Add](https://cmake.org/cmake/help/latest/module/ExternalProject.html)
with a custom target that drives download, build and deployment of the dependency.

## Writing Benchmarks

Google Benchmark provides facilities to avoid optimizing away some portions of the code or
effects on variables. It might get bypassed by compilers in the future though, as they get
smarter. Thus, providing some side effect outside of benchmarked section is advised.

---

### Using Google Benchmark

Overall structure of the file should look roughly like this:

<includes>
<function(s)>
BENCHMARK(function1_name)
BENCHMARK(function2_name)
...

BENCHMARK_MAIN()

Lets have a closer look at a benchmarking function:

void function_name(benchmark::State& state) {
<setup code, to not be included into timings>

for (auto _: state) {
<one iteration of a code to be benchmarked>
}
<side effect on variables that are used in the benchmark>
}

Do note that as long as `_` is in scope, the runtime will be counted towards benchmarking results,
whereas the rest will not be included. Not writing a side effect (like a check can throw,
or printing on the console) might result in code being optimized away, thus making whole
benchmarking function useless.

---

### Building the benchmarks

To build a benchmark, simply put the benchmark `.cpp` file into `<GIL root>/benchmark`
and reconfigure the project.
Then, the targets to build are `benchmark_<cpp filename without extension>`
(`benchmark_ocv_<name>` and `benchmark_ipp_<name>` for the OpenCV and IPP
comparison benchmarks under `opencv/` and `ipp/`).

---

### Running benchmarks

Pairing the benchmarking framework with a good plotting tool of your choice is advised.
It provides options to control which benchmarks to run, for how long,
and output formats. Running `--help` on executables will print available options.
45 changes: 45 additions & 0 deletions benchmark/ipp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright (C) 2020 Samuel Debionne, ESRF.

# Use, modification and distribution is subject to the Boost Software
# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)

#-----------------------------------------------------------------------------
# Find package IPP
#-----------------------------------------------------------------------------
#find_package(IPP REQUIRED)
find_path(IPP_INCLUDE_DIR ipp.h PATHS ${IPP_ROOT}/include)
message("IPP_INCLUDE_DIR: ${IPP_INCLUDE_DIR}")
find_library(IPP_LIB_CORE ippcore PATHS ${IPP_ROOT}/lib)
find_library(IPP_LIB_I ippi PATHS ${IPP_ROOT}/lib)
message("IPP_LIB_CORE: ${IPP_LIB_CORE}")
message("IPP_LIB_I: ${IPP_LIB_I}")
add_library(ipp::ipp INTERFACE IMPORTED)
target_include_directories(ipp::ipp INTERFACE ${IPP_INCLUDE_DIR})
target_link_libraries(ipp::ipp INTERFACE ${IPP_LIB_CORE} ${IPP_LIB_I})

#-----------------------------------------------------------------------------
# Build benchmarks
#-----------------------------------------------------------------------------
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
file(GLOB _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp CONFIGURE_DEPEND)
else()
file(GLOB _benchmarks ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
endif()

foreach(_benchmark ${_benchmarks})
get_filename_component(_name ${_benchmark} NAME_WE)
set(_target benchmark_ipp_${_name})
add_executable(${_target} ${_name}.cpp)
target_compile_definitions(${_target} PRIVATE BOOST_GIL_USE_CONCEPT_CHECK=1)
target_link_libraries(${_target}
PRIVATE
gil_compile_options
gil_include_directories
gil_dependencies
gil_googlebenchmark
ipp::ipp)
endforeach()

unset(_benchmarks)
unset(_benchmark)
141 changes: 141 additions & 0 deletions benchmark/ipp/view_flip.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright (C) 2020 Samuel Debionne, ESRF.

// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <benchmark/benchmark.h>

#include <ipp.h>

#include <boost/gil.hpp>

inline bool is_odd(benchmark::IterationCount cnt) { return (cnt % 2); }

static void ipp_flip_left_right(benchmark::State& state)
{
using namespace boost::gil;

size_t dim = state.range(0);

gray8_image_t in(dim, dim);
generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; });
gray8_image_t out(dim, dim);

IppiSize srcRoi = { dim, dim };

for (auto _ : state) {
// The code to benchmark
auto res = ippiMirror_8u_C1R(
boost::gil::interleaved_view_get_raw_data(const_view(in)), (int)const_view(in).pixels().row_size(),
boost::gil::interleaved_view_get_raw_data(view(out)), (int)view(in).pixels().row_size(),
srcRoi,
ippAxsVertical);

if (res != ippStsNoErr)
state.SkipWithError("ipp_flip_left_right failed");
}

if (!equal_pixels(flipped_left_right_view(const_view(in)), const_view(out)))
state.SkipWithError("ipp_flip_left_right wrong result");
}
BENCHMARK(ipp_flip_left_right)->RangeMultiplier(2)->Range(256, 8 << 10);

static void ipp_flip_left_right_inplace(benchmark::State& state)
{
using namespace boost::gil;

size_t dim = state.range(0);

gray8_image_t in(dim, dim);
generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; });
gray8_image_t in_ref(in);

IppiSize srcRoi = { dim, dim };

for (auto _ : state) {
// The code to benchmark
auto res = ippiMirror_8u_C1IR(
boost::gil::interleaved_view_get_raw_data(view(in)), (int)view(in).pixels().row_size(),
srcRoi,
ippAxsVertical);

if (res != ippStsNoErr)
state.SkipWithError("ipp_flip_left_right_inplace failed");
}

if (is_odd(state.iterations())) {
if (!equal_pixels(flipped_left_right_view(const_view(in_ref)), const_view(in)))
state.SkipWithError("ipp_flip_left_right_inplace wrong result");
}
else {
if (!equal_pixels(const_view(in_ref), const_view(in)))
state.SkipWithError("ipp_flip_left_right_inplace wrong result");
}
}
BENCHMARK(ipp_flip_left_right_inplace)->RangeMultiplier(2)->Range(256, 8 << 10);

static void ipp_flip_up_down(benchmark::State& state)
{
using namespace boost::gil;

size_t dim = state.range(0);

gray8_image_t in(dim, dim);
generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; });
gray8_image_t out(dim, dim);

IppiSize srcRoi = { dim, dim };

for (auto _ : state) {
// The code to benchmark
auto res = ippiMirror_8u_C1R(
boost::gil::interleaved_view_get_raw_data(const_view(in)), (int)const_view(in).pixels().row_size(),
boost::gil::interleaved_view_get_raw_data(view(out)), (int)view(out).pixels().row_size(),
srcRoi,
ippAxsHorizontal);

if (res != ippStsNoErr)
state.SkipWithError("ipp_flip_up_down failed");
}

if (is_odd(state.iterations())) {
if (!equal_pixels(flipped_up_down_view(const_view(in)), const_view(out)))
state.SkipWithError("ipp_flip_up_down wrong result");
}
else {
if (!equal_pixels(const_view(in), const_view(out)))
state.SkipWithError("ipp_flip_up_down wrong result");
}
}
BENCHMARK(ipp_flip_up_down)->RangeMultiplier(2)->Range(256, 8 << 10);

static void ipp_flip_up_down_inplace(benchmark::State& state)
{
using namespace boost::gil;

size_t dim = state.range(0);

gray8_image_t in(dim, dim);
generate_pixels(view(in), [i = 0]() mutable -> std::uint8_t { return ++i; });
gray8_image_t in_ref(in);

IppiSize srcRoi = { dim, dim };

for (auto _ : state) {
// The code to benchmark
auto res = ippiMirror_8u_C1IR(
boost::gil::interleaved_view_get_raw_data(view(in)), (int)view(in).pixels().row_size(),
srcRoi,
ippAxsHorizontal);

if (res != ippStsNoErr)
state.SkipWithError("ipp_flip_up_down_inplace failed");
}

if (!equal_pixels(flipped_up_down_view(const_view(in_ref)), const_view(in)))
state.SkipWithError("ipp_flip_up_down_inplace wrong result");
}
BENCHMARK(ipp_flip_up_down_inplace)->RangeMultiplier(2)->Range(256, 8 << 10);

BENCHMARK_MAIN();
Loading
Loading