Skip to content

👷 Add benchmark to ci #80

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 11, 2025
Merged
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
18 changes: 14 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,25 @@ jobs:
with:
compiler_profile_url: https://github.com/libhal/arm-gnu-toolchain.git
compiler_profile: v1/arm-gcc-12.3
platform_profile_url: https://github.com/libhal/libhal-lpc40.git
platform_profile: v2/lpc4078
platform_profile_url: https://github.com/libhal/libhal-arm-mcu.git
platform_profile: v1/lpc4078
secrets: inherit

demo_check_stm32f103c8:
uses: libhal/ci/.github/workflows/demo_builder.yml@5.x.y
with:
compiler_profile_url: https://github.com/libhal/arm-gnu-toolchain.git
compiler_profile: v1/arm-gcc-12.3
platform_profile_url: https://github.com/libhal/libhal-stm32f1.git
platform_profile: v2/stm32f103c8
platform_profile_url: https://github.com/libhal/libhal-arm-mcu.git
platform_profile: v1/stm32f103c8
secrets: inherit

build_benchmarks:
uses: libhal/ci/.github/workflows/app_builder.yml@5.x.y
with:
compiler_profile_url: https://github.com/libhal/arm-gnu-toolchain.git
compiler_profile: v1/arm-gcc-12.3
platform_profile_url: https://github.com/libhal/libhal-arm-mcu.git
platform_profile: v1/stm32f103c8
conan_build_dir: benchmark
secrets: inherit
7 changes: 2 additions & 5 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

cmake_minimum_required(VERSION 3.25)

project(benchmark-error-handling LANGUAGES CXX)
project(benchmark_error_handling LANGUAGES CXX)

# Always check that the $ENV{LIBHAL_PLATFORM_LIBRARY} & $ENV{LIBHAL_PLATFORM}
# environment variables are set by the profile.
Expand All @@ -31,10 +31,6 @@ endif()
find_package(libhal-$ENV{LIBHAL_PLATFORM_LIBRARY} REQUIRED)
find_package(prebuilt-picolibc QUIET)

if(${CMAKE_CROSSCOMPILING})
find_package(${PACKAGE} REQUIRED)
endif()

set(startup_source_files main.cpp filler.cpp)

# Makes the platform/<platform_name>.cpp file optional
Expand Down Expand Up @@ -73,6 +69,7 @@ foreach(test_path ${GENERATED_TEST})
message(STATUS "Generating Demo for \"${elf}\"")
add_executable(${elf} ${test_path})

target_include_directories(${elf} PRIVATE .)
target_compile_options(${elf} PRIVATE
-g
-Werror
Expand Down
2 changes: 1 addition & 1 deletion benchmark/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ def requirements(self):
bootstrap = self.python_requires["libhal-bootstrap"]
bootstrap.module.add_demo_requirements(self)
if self.options.platform != "mac":
self.requires("libhal-exceptions/[1.2.0 || latest]")
self.requires("libhal-exceptions/1.2.0")
25 changes: 20 additions & 5 deletions benchmark/filler.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2024 - 2025 Khalil Estell and the libhal contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* @file filler.cpp
*
Expand All @@ -10,8 +24,9 @@
*
*/

/**
* @brief Performs some set of actions that forces the compiler to link in all
* of the filler functions.
*/
void activate_filler();
#include <platform.hpp>

void link_filler_functions()
{
// TODO(79): Fill in file with linker information
}
86 changes: 66 additions & 20 deletions benchmark/generate.py → benchmark/gen_depth_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ def generate_exception_file(destructor_percent: int, depth: int) -> str:
#include <cstdint>
#include <string_view>

// External functions
extern void start();
extern void end();
extern void log_start(std::string_view);
#include <platform.hpp>

// Global side effect to prevent optimization
std::int32_t volatile side_effect = 0;
Expand Down Expand Up @@ -84,10 +81,24 @@ class destructor_object {{

# Generate function implementations
for i in range(depth, 0, -1):
# Determine if this function should have a destructor
has_destructor = i <= destructor_count
obj_type = "destructor_object" if has_destructor else "simple_object"
# Calculate which functions should have destructors - evenly distributed
function_index = depth - i # 0-based index for this function

# Calculate total destructor count and distribute evenly
destructor_count = int((destructor_percent / 100.0) * depth)

if destructor_count == 0:
has_destructor = False
elif destructor_count >= depth:
has_destructor = True
else:
# Even distribution using integer arithmetic
current_position = function_index * destructor_count // depth
next_position = (function_index + 1) * destructor_count // depth
has_destructor = next_position > current_position

obj_type = "destructor_object" if has_destructor else "simple_object"
obj_type = "destructor_object" if has_destructor else "simple_object"
content += f'''[[gnu::noinline]]
int depth_{i:02d}() {{
{obj_type} obj(side_effect >> 8);
Expand Down Expand Up @@ -133,11 +144,38 @@ class destructor_object {{
side_effect = 1; // Ensure we will throw

try {{
depth_{depth:02d}();
}} catch (const test_error& e) {{
depth_70();
}} catch (test_error const& e) {{
end();
}}

pause();

try {{
depth_50();
}} catch (test_error const& e) {{
end();
}}

pause();

try {{
depth_30();
}} catch (test_error const& e) {{
end();
}}

pause();

try {{
depth_10();
}} catch (test_error const& e) {{
end();
}}

pause();
}}

'''

return content
Expand All @@ -154,10 +192,7 @@ def generate_result_file(destructor_percent: int, depth: int) -> str:
#include <expected>
#include <string_view>

// External functions
extern void start();
extern void end();
extern void log_start(std::string_view);
#include <platform.hpp>

// Global side effect to prevent optimization
std::int32_t volatile side_effect = 0;
Expand Down Expand Up @@ -261,11 +296,23 @@ class destructor_object {{
# Generate test runner
content += f'''// Test runner
void run_test() {{
log_start("RESULT_{destructor_percent}PCT_DEPTH{depth}");
log_start("RESULT-{destructor_percent}%-DEPTH{depth}");
side_effect = 1; // Ensure we will return error

auto result = depth_{depth:02d}();
if (!result) {{
auto result_70 = depth_70();
if (!result_70) {{
end();
}}
auto result_50 = depth_50();
if (!result_50) {{
end();
}}
auto result_30 = depth_30();
if (!result_30) {{
end();
}}
auto result_10 = depth_10();
if (!result_10) {{
end();
}}
}}
Expand Down Expand Up @@ -303,15 +350,14 @@ def main():
"""Generate test files for all combinations."""

destructor_percentages = [0, 25, 50, 75, 100]
depths = [10, 30, 50, 70]
max_depth = 70
output_dir = Path("generated_tests")

for destructor_pct in destructor_percentages:
for depth in depths:
generate_test_files(destructor_pct, depth, output_dir)
generate_test_files(destructor_pct, max_depth, output_dir)

print(
f"\nGenerated {len(destructor_percentages) * len(depths) * 2} test files in {output_dir}/")
f"\nGenerated {len(destructor_percentages) * 2} test files in {output_dir}/")


if __name__ == "__main__":
Expand Down
Loading
Loading