From 96554aa4cdcbc8a86e12ef552190cd8cb8e08f42 Mon Sep 17 00:00:00 2001 From: FranckRJ Date: Sun, 26 May 2024 23:00:14 +0200 Subject: [PATCH] Added a tool to help benchmark FakeIt compilation speed. --- .gitignore | 3 + benchmark/README.md | 15 ++++ benchmark/create_benchmark.py | 140 ++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 benchmark/README.md create mode 100644 benchmark/create_benchmark.py diff --git a/.gitignore b/.gitignore index 746f57a2..3b0e6a46 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,6 @@ build/ # Testing Testing/ + +# Benchmark +benchmark/bench*/ diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 00000000..2bf356e8 --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,15 @@ +First create the benchmark files: +```bash +python3 create_benchmark.py bench/ +``` +_Note: you can check the help of the script for additional options._ + +Then generate the build files: +```bash +cmake -S bench/ -B bench/build -DCMAKE_BUILD_TYPE=Debug +``` + +Then build with a tool that helps you time the build, like the linux command `time`: +```bash +time cmake --build bench/build -j +``` diff --git a/benchmark/create_benchmark.py b/benchmark/create_benchmark.py new file mode 100644 index 00000000..83d3430a --- /dev/null +++ b/benchmark/create_benchmark.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import argparse +import os +import pathlib +from dataclasses import dataclass + +fakeit_path = os.path.join(os.path.dirname(__file__), "..") + + +@dataclass +class ProjectStructure: + gen_dir: os.PathLike + single_header_dir: os.PathLike + files: int + interfaces: int + methods: int + stubs: int + + +def create_project(project_structure: ProjectStructure): + pathlib.Path(project_structure.gen_dir).mkdir(parents=True, exist_ok=True) + # Test file + for file_idx in range(0, project_structure.files): + with open( + os.path.join(project_structure.gen_dir, f"file_{file_idx}.cpp"), "w" + ) as file: + file.write("#include \n") + file.write(f'#include "file_{file_idx}.hpp"\n') + file.write("using namespace fakeit;\n") + file.write("namespace {\n") + # Interface + for interface_idx in range(0, project_structure.interfaces): + file.write(f"class Interface_{interface_idx} {{\n") + file.write("public:\n") + # Method + for method_idx in range(0, project_structure.methods): + file.write(f" virtual int method_{method_idx}(int) = 0;\n") + file.write("};\n") + # Test of methods + file.write(f"void test_interface_{interface_idx}() {{\n") + file.write(f" Mock mock;\n") + for method_idx in range(0, project_structure.methods): + for stub_idx in range(0, project_structure.stubs): + file.write( + f" When(Method(mock, method_{method_idx}).Using({stub_idx})).Return({stub_idx});\n" + ) + for method_idx in range(0, project_structure.methods): + for stub_idx in range(0, project_structure.stubs): + file.write(f" mock.get().method_{method_idx}({stub_idx});\n") + for method_idx in range(0, project_structure.methods): + for stub_idx in range(0, project_structure.stubs): + file.write( + f" Verify(Method(mock, method_{method_idx}).Using({stub_idx})).Exactly(1);\n" + ) + file.write("}\n") + file.write("}\n") + # Test of whole file + file.write(f"void test_file_{file_idx}() {{\n") + for interface_idx in range(0, project_structure.interfaces): + file.write(f" test_interface_{interface_idx}();\n") + file.write("}\n") + # Header of test file + with open( + os.path.join(project_structure.gen_dir, f"file_{file_idx}.hpp"), "w" + ) as file: + file.write("#pragma once\n") + file.write(f"void test_file_{file_idx}();\n") + # Main + with open(os.path.join(project_structure.gen_dir, "main.cpp"), "w") as file: + for file_idx in range(0, project_structure.files): + file.write(f'#include "file_{file_idx}.hpp"\n') + file.write("int main() {\n") + for file_idx in range(0, project_structure.files): + file.write(f" test_file_{file_idx}();\n") + file.write("}\n") + # CMake file + with open(os.path.join(project_structure.gen_dir, "CMakeLists.txt"), "w") as file: + file.write("cmake_minimum_required(VERSION 3.14)\n") + file.write("project(FakeIt-bench LANGUAGES CXX)\n") + file.write("add_executable(bench\n") + for file_idx in range(0, project_structure.files): + file.write(f" file_{file_idx}.cpp\n") + file.write(f" main.cpp\n") + file.write(")\n") + file.write( + f'target_include_directories(bench PRIVATE "{project_structure.single_header_dir}")\n' + ) + + +def get_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "directory", + type=str, + help="Directory where the benchmark project will be created.", + ) + parser.add_argument( + "-d", + "--header-dir", + type=str, + default=os.path.join(fakeit_path, "single_header", "standalone"), + help="The directory containing the fakeit single header.", + ) + parser.add_argument( + "-f", "--files", type=int, default=10, help="Number of C++ files to create." + ) + parser.add_argument( + "-i", + "--interfaces", + type=int, + default=10, + help="Number of interfaces per file.", + ) + parser.add_argument( + "-m", "--methods", type=int, default=10, help="Number of methods per interface." + ) + parser.add_argument( + "-s", "--stubs", type=int, default=10, help="Number of stubs per methods." + ) + return parser + + +def main(): + args = get_parser().parse_args() + create_project( + ProjectStructure( + args.directory, + args.header_dir, + args.files, + args.interfaces, + args.methods, + args.stubs, + ) + ) + + +if __name__ == "__main__": + main()