Skip to content

Commit

Permalink
Merge pull request #10 from stevenewald/bench
Browse files Browse the repository at this point in the history
Added benchmarks and ARM NEON support
  • Loading branch information
stevenewald authored Nov 27, 2024
2 parents 7cf35e8 + b0e8ecc commit 8f115c2
Show file tree
Hide file tree
Showing 35 changed files with 733 additions and 345 deletions.
33 changes: 27 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ include(cmake/project-is-top-level.cmake)
include(cmake/variables.cmake)

set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -mavx512f -mavx512dq -mavx512vl -mavx512bf16")
add_compile_options(-fno-inline -fno-omit-frame-pointer)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
# add_compile_options(-fno-inline -fno-omit-frame-pointer)


# ---- Declare library ----
Expand All @@ -27,17 +27,37 @@ add_library(
source/mandelbrot/mandelbrot_window.cpp
source/graphics/color_conversions/color_conversions.cpp
source/graphics/aspect_ratio/aspect_ratio.cpp
source/mandelbrot/equations_simd.cpp
source/mandelbrot/equations.cpp
source/units/coordinates.cpp
)

include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-mavx512f -mavx512dq -mavx512vl -mavx512bf16" HAS_AVX512)
check_cxx_source_compiles("
#include <arm_neon.h>
int main() {
float32x4_t vec = vdupq_n_f32(0.0f);
return 0;
}" HAS_NEON)

if (HAS_ALL_AVX512)
message(STATUS "AVX-512 is supported by the compiler.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx512f -mavx512dq -mavx512vl -mavx512bf16")
target_sources(fractal-generator_lib PRIVATE source/mandelbrot/equations_simd.cpp)
elseif(HAS_NEON)
message(STATUS "ARM NEON is supported by the compiler.")
target_sources(fractal-generator_lib PRIVATE source/mandelbrot/equations_neon.cpp)
else()
message(STATUS "SIMD is not fully supported by the compiler. SIMD will not be enabled.")
target_sources(fractal-generator_lib PRIVATE source/mandelbrot/equations_compat.cpp)
endif()

target_include_directories(
fractal-generator_lib ${warning_guard}
PUBLIC
"\$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/source>"
)

target_compile_features(fractal-generator_lib PUBLIC cxx_std_20)
target_compile_features(fractal-generator_lib PUBLIC cxx_std_23)

find_package(fmt REQUIRED)
find_package(argparse REQUIRED)
Expand All @@ -55,8 +75,9 @@ add_executable(fractal-generator::exe ALIAS fractal-generator_exe)

set_property(TARGET fractal-generator_exe PROPERTY OUTPUT_NAME fractal-generator)

target_compile_features(fractal-generator_exe PRIVATE cxx_std_20)
target_compile_features(fractal-generator_exe PRIVATE cxx_std_23)

target_link_libraries(fractal-generator_exe PRIVATE fmt::fmt)
target_link_libraries(fractal-generator_exe PRIVATE fractal-generator_lib)
target_link_libraries(fractal-generator_exe PRIVATE argparse::argparse)
target_link_libraries(fractal-generator_exe PRIVATE sfml-graphics)
Expand Down
6 changes: 3 additions & 3 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_EXTENSIONS": "OFF",
"CMAKE_CXX_STANDARD": "20",
"CMAKE_CXX_STANDARD": "23",
"CMAKE_CXX_STANDARD_REQUIRED": "ON"
}
},
Expand All @@ -59,7 +59,7 @@
"description": "These flags are supported by both GCC and Clang",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_FLAGS": "-D_GLIBCXX_ASSERTIONS=1 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast",
"CMAKE_CXX_FLAGS": "-D_GLIBCXX_ASSERTIONS=1 -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast",
"CMAKE_EXE_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now,-z,nodlopen",
"CMAKE_SHARED_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now,-z,nodlopen"
}
Expand All @@ -68,7 +68,7 @@
"name": "flags-appleclang",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_FLAGS": "-fstack-protector-strong -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast"
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast"
}
},
{
Expand Down
15 changes: 10 additions & 5 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ version: '3'

vars:
NAME: fractal-generator
RELEASE_BUILD: "false"
RELEASE_PRESET: "ci-ubuntu"
RELEASE_BUILD: "true"
RELEASE_PRESET: "ci-macos"

tasks:
deps:
Expand All @@ -14,12 +14,11 @@ tasks:

init:
dir: .
vars:
PRESET: '{{if eq .RELEASE_BUILD "true"}}{{.RELEASE_PRESET}}{{else}}dev{{end}}'
preconditions:
- test -f CMakeUserPresets.json
cmds:
- cmake --preset={{.PRESET}}
- cmake --preset=dev
- cmake --preset={{.RELEASE_PRESET}} -DCMAKE_BUILD_TYPE=Release


build:
Expand Down Expand Up @@ -57,6 +56,12 @@ tasks:
- task: build
- ctest {{.TEST_FLAGS}}

bench:
dir: .
cmds:
- task: build
- ./build/benchmark/fractal_benchmarks --benchmark_repetitions=5 --benchmark_min_warmup_time=0.5 --benchmark_report_aggregates_only=true

fmt:
dir: .
cmds:
Expand Down
37 changes: 37 additions & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
project(FractalBenchmarks LANGUAGES CXX)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
# ---- Dependencies ----

find_package(benchmark REQUIRED)

# ---- Benchmarks ----
add_executable(fractal_benchmarks
source/bench.cpp
source/main.cpp
)

target_include_directories(
fractal_benchmarks ${warning_guard}
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/source>"
)

target_link_libraries(
fractal_benchmarks PRIVATE
fractal-generator_lib
sfml-graphics
benchmark::benchmark
)

# Enable lto for release builds
include(CheckIPOSupported)
check_ipo_supported(RESULT lto_supported OUTPUT error)
if(lto_supported)
set_target_properties(fractal_benchmarks PROPERTIES INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
else()
message(WARNING "LTO is not supported: ${error}")
endif()

target_compile_features(fractal_benchmarks PRIVATE cxx_std_23)
35 changes: 35 additions & 0 deletions benchmark/source/bench.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "config.hpp"
#include "units/coordinates.hpp"
#include "graphics/display_to_complex.hpp"
#include "mandelbrot/equations.hpp"
#include "mandelbrot/equations.hpp"
#include "units/units.hpp"

#include <benchmark/benchmark.h>

using namespace fractal;

static void BM_GenerateMandelbrotSimd(benchmark::State& state)
{
DisplayDomain display = {
{0, 0 },
{800, 800}
};
complex_domain complex = START_COMPLEX_DOMAIN;
avx512_complex start{};
DisplayToComplexCoordinates t{display, complex};

std::int64_t prox = 0;
for (auto _ : state) {
for (auto it = display.begin(); it != display.end(); it += 8) {
benchmark::DoNotOptimize(
compute_iterations(start, t.to_complex_projections(*it), 100)
);
}
prox += display.size();
}
state.SetItemsProcessed(prox);
}

BENCHMARK(BM_GenerateMandelbrotSimd)->Arg(0);
// BENCHMARK(BM_GenerateMandelbrotSimd)->Arg(100000);
2 changes: 2 additions & 0 deletions benchmark/source/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include <benchmark/benchmark.h>
BENCHMARK_MAIN();
1 change: 1 addition & 0 deletions cmake/dev-mode.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ include(cmake/folders.cmake)

include(CTest)
add_subdirectory(test)
add_subdirectory(benchmark)

add_custom_target(
run-exe
Expand Down
1 change: 1 addition & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ def configure(self):

def build_requirements(self):
self.test_requires("catch2/3.7.0")
self.test_requires("benchmark/1.9.0");
13 changes: 4 additions & 9 deletions source/config.hpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
#pragma once

#include "coordinates.hpp"
#include "units.hpp"
#include "units/coordinates.hpp"
#include "units/units.hpp"

#include <cstddef>

namespace fractal {

constexpr std::size_t WINDOW_WIDTH = 5120;
constexpr std::size_t WINDOW_HEIGHT = 1440;
constexpr std::size_t WINDOW_WIDTH = 800UZ;
constexpr std::size_t WINDOW_HEIGHT = 600UZ;
constexpr std::size_t FRAME_RATE = 60UZ;

constexpr display_domain DISPLAY_DOMAIN{
{0, 0 },
{WINDOW_WIDTH - 1, WINDOW_HEIGHT - 1}
};

constexpr complex_domain START_COMPLEX_DOMAIN{
{complex_underlying{-1.402}, complex_underlying{-.001}},
{complex_underlying{-1.400}, complex_underlying{.001} }
Expand Down
38 changes: 26 additions & 12 deletions source/graphics/aspect_ratio/aspect_ratio.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#include "aspect_ratio.hpp"

#include "units/display_domain.hpp"

namespace fractal {

display_coordinate calculate_rectangle_end_point(
DisplayDomain calculate_rectangle_end_points(
display_coordinate start, display_coordinate current, float target_aspect_ratio
)
{
auto width = static_cast<float>(std::abs(current.first - start.first));
auto height = static_cast<float>(std::abs(current.second - start.second));
auto width = static_cast<float>(std::abs(current.x - start.x));
auto height = static_cast<float>(std::abs(current.y - start.y));

// Adjust the dimensions to maintain the target aspect ratio
if (width / height > target_aspect_ratio) {
Expand All @@ -19,18 +21,30 @@ display_coordinate calculate_rectangle_end_point(
height = width / target_aspect_ratio;
}

auto x = static_cast<float>(std::min(current.first, start.first));
auto y = static_cast<float>(std::min(current.second, start.second));
auto x_pos1 = static_cast<float>(start.x);
auto y_pos1 = static_cast<float>(start.y);
auto x_pos2 = x_pos1 + width;
auto y_pos2 = y_pos1 + height;

bool flipped_horizontal = start.x > current.x;
bool flipped_vertical = start.y > current.y;

// Adjust the top-left corner based on new dimensions
if (current.first < start.first) {
x = static_cast<float>(start.first) - width;
if (flipped_vertical) {
y_pos1 -= height;
y_pos2 -= height;
}
if (current.second < start.second) {
y = static_cast<float>(start.second) - height;
if (flipped_horizontal) {
x_pos1 -= width;
x_pos2 -= width;
}

// Return the top-left and bottom-right corners as a pair of sf::Vector2f
return {x + width, y + height};
display_coordinate top_left{
static_cast<uint16_t>(x_pos1), static_cast<uint16_t>(y_pos1)
};
display_coordinate bottom_right{
static_cast<uint16_t>(x_pos2), static_cast<uint16_t>(y_pos2)
};

return {top_left, bottom_right};
}
} // namespace fractal
5 changes: 3 additions & 2 deletions source/graphics/aspect_ratio/aspect_ratio.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#pragma once

#include "config.hpp"
#include "coordinates.hpp"
#include "units/coordinates.hpp"
#include "units/display_domain.hpp"

namespace fractal {
display_coordinate calculate_rectangle_end_point(
DisplayDomain calculate_rectangle_end_points(
display_coordinate start, display_coordinate current,
float target_aspect_ratio = static_cast<float>(WINDOW_WIDTH) / WINDOW_HEIGHT
);
Expand Down
20 changes: 8 additions & 12 deletions source/graphics/color_conversions/color_conversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
#include <stdexcept>

namespace fractal {
color hsv_to_rgb(float hue, float saturation, float value)
color hsv_to_rgb(Hue hue, Percentage saturation, Percentage value)
{
float chroma = value * saturation;
float hue_prime = hue / 60.0f;
float chroma = value.get_percentage() * saturation.get_percentage();
float hue_prime = hue.get_hue() / 60.0f;
float uint16_termediate =
chroma * (1 - static_cast<float>(std::fabs(std::fmod(hue_prime, 2) - 1)));
float red_temp = 0.0f;
Expand Down Expand Up @@ -42,7 +42,7 @@ color hsv_to_rgb(float hue, float saturation, float value)
blue_temp = uint16_termediate;
}

float match_value = value - chroma;
float match_value = value.get_percentage() - chroma;
float red = red_temp + match_value;
float green = green_temp + match_value;
float blue = blue_temp + match_value;
Expand All @@ -54,15 +54,11 @@ color hsv_to_rgb(float hue, float saturation, float value)
return {red_int, green_int, blue_int};
}

color ratio_to_rgb(float ratio)
color ratio_to_rgb(Percentage ratio)
{
if (ratio < 0 || ratio > 1) [[unlikely]] {
throw std::out_of_range(fmt::format("Ratio out of range: {}", ratio));
}

float hue = ratio * 360.0f;
float saturation = 1.0f;
float value = 1.0f;
Hue hue{ratio.get_percentage() * 360.0f};
Percentage saturation{1.0f};
Percentage value{1.0f};

return hsv_to_rgb(hue, saturation, value);
}
Expand Down
10 changes: 7 additions & 3 deletions source/graphics/color_conversions/color_conversions.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#pragma once

#include "units.hpp"
#include "units/units.hpp"
#include "util.hpp"

#include <cassert>

namespace fractal {
color hsv_to_rgb(float hue, float saturation, float value);

color ratio_to_rgb(float ratio);
color hsv_to_rgb(Hue hue, Percentage saturation, Percentage value);

color ratio_to_rgb(Percentage ratio);
} // namespace fractal
2 changes: 1 addition & 1 deletion source/graphics/display/display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void PixelDisplay::handle_event_(const sf::Event& event)
}
}

void PixelDisplay::add_observer(std::unique_ptr<DisplayEventObserver> observer)
void PixelDisplay::add_drawable(std::unique_ptr<DisplayEventObserver> observer)
{
observers_.push_back(std::move(observer));
}
Expand Down
2 changes: 1 addition & 1 deletion source/graphics/display/display.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class PixelDisplay {
public:
explicit PixelDisplay();

void add_observer(std::unique_ptr<DisplayEventObserver> observer);
void add_drawable(std::unique_ptr<DisplayEventObserver> observer);

void poll_window_events();
void display_window();
Expand Down
Loading

0 comments on commit 8f115c2

Please sign in to comment.