├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── XcodeProperty.cmake ├── src ├── yybench.h ├── yybench_chart.c ├── yybench_chart.h ├── yybench_cpu.c ├── yybench_cpu.h ├── yybench_def.h ├── yybench_env.c ├── yybench_env.h ├── yybench_file.c ├── yybench_file.h ├── yybench_perf.c ├── yybench_perf.h ├── yybench_rand.c ├── yybench_rand.h ├── yybench_str.c ├── yybench_str.h ├── yybench_time.c └── yybench_time.h └── test └── yybench_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake 2 | /build 3 | CMakeLists.txt.user 4 | CMakeCache.txt 5 | CMakeFiles 6 | CMakeScripts 7 | Testing 8 | Makefile 9 | cmake_install.cmake 10 | install_manifest.txt 11 | compile_commands.json 12 | CTestTestfile.cmake 13 | _deps 14 | 15 | # GCC coverage testing tool files 16 | *.gcno 17 | *.gcda 18 | *.gcov 19 | 20 | # JetBrains 21 | .idea 22 | 23 | # Xcode 24 | /xcode 25 | .build/ 26 | xcuserdata/ 27 | *.xcscmblueprint 28 | *.xccheckout 29 | DerivedData/ 30 | *.moved-aside 31 | *.pbxuser 32 | !default.pbxuser 33 | *.mode1v3 34 | !default.mode1v3 35 | *.mode2v3 36 | !default.mode2v3 37 | *.perspectivev3 38 | !default.perspectivev3 39 | *.hmap 40 | *.ipa 41 | *.dSYM.zip 42 | *.dSYM 43 | Carthage/Build/ 44 | fastlane/report.xml 45 | fastlane/Preview.html 46 | fastlane/screenshots/**/*.png 47 | fastlane/test_output 48 | iOSInjectionProject/ 49 | 50 | # Prerequisites 51 | *.d 52 | 53 | # Object files 54 | *.o 55 | *.ko 56 | *.obj 57 | *.elf 58 | 59 | # Linker output 60 | *.ilk 61 | *.map 62 | *.exp 63 | 64 | # Libraries 65 | *.lib 66 | *.a 67 | *.la 68 | *.lo 69 | 70 | # Shared objects (inc. Windows DLLs) 71 | *.dll 72 | *.so 73 | *.so.* 74 | *.dylib 75 | 76 | # Executables 77 | *.exe 78 | *.out 79 | *.app 80 | *.i*86 81 | *.x86_64 82 | *.hex 83 | 84 | # Debug files 85 | *.dSYM/ 86 | *.su 87 | *.idb 88 | *.pdb 89 | 90 | # OS 91 | .DS_Store 92 | .Spotlight-V100 93 | .Trashes 94 | Thumbs.db 95 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.5) 2 | project (yybench VERSION 1.0.0) 3 | 4 | # Options 5 | option(YYBENCH_BUILD_TESTS "Build tests" OFF) 6 | 7 | 8 | # Build Type 9 | if (XCODE OR MSVC) 10 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE) 11 | endif() 12 | if(NOT CMAKE_BUILD_TYPE) 13 | message(STATUS "No build type selected, default to: Release") 14 | set(CMAKE_BUILD_TYPE Release) 15 | endif() 16 | 17 | 18 | # Library 19 | file(GLOB SOURCES "src/*.h" "src/*.c") 20 | add_library(yybench ${SOURCES}) 21 | target_include_directories(yybench PUBLIC src) 22 | 23 | # Tests 24 | if(YYBENCH_BUILD_TESTS) 25 | add_executable(yybench_test "test/yybench_test.c") 26 | target_link_libraries(yybench_test yybench) 27 | endif() 28 | 29 | # Project Config 30 | if(XCODE) 31 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 32 | include(XcodeProperty) 33 | 34 | set_default_xcode_property(yybench) 35 | set_xcode_deployment_version(yybench "10.11" "9.0" "9.0" "2.0") 36 | if(YYBENCH_BUILD_TESTS) 37 | set_default_xcode_property(yybench_test) 38 | endif() 39 | endif() 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 YaoYuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yybench 2 | 3 | A microbenchmark library for C, support Linux, macOS, Windows, Android, iOS. 4 | 5 | Work in progress... 6 | -------------------------------------------------------------------------------- /cmake/XcodeProperty.cmake: -------------------------------------------------------------------------------- 1 | # This module contains some macros for Xcode project 2 | 3 | 4 | # Set Xcode property to target 5 | macro(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE) 6 | set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE}) 7 | endmacro(set_xcode_property) 8 | 9 | 10 | # Set default Xcode properties to target 11 | macro(set_default_xcode_property TARGET) 12 | 13 | # Standard 14 | set_xcode_property(${TARGET} GCC_C_LANGUAGE_STANDARD "gnu99") 15 | set_xcode_property(${TARGET} CLANG_CXX_LANGUAGE_STANDARD "gnu++11") 16 | set_xcode_property(${TARGET} CLANG_CXX_LIBRARY "libc++") 17 | 18 | # Compiler Flags 19 | set_xcode_property(${TARGET} OTHER_CFLAGS[variant=Debug] " ") 20 | set_xcode_property(${TARGET} OTHER_CFLAGS[variant=Release] " ") 21 | set_xcode_property(${TARGET} OTHER_CPLUSPLUSFLAGS[variant=Debug] "$(OTHER_CFLAGS)") 22 | set_xcode_property(${TARGET} OTHER_CPLUSPLUSFLAGS[variant=Release] "$(OTHER_CFLAGS)") 23 | 24 | # Macros 25 | set_xcode_property(${TARGET} GCC_PREPROCESSOR_DEFINITIONS[variant=Debug] "DEBUG=1") 26 | set_xcode_property(${TARGET} GCC_PREPROCESSOR_DEFINITIONS[variant=Release] " ") 27 | 28 | # Optimization 29 | set_xcode_property(${TARGET} GCC_OPTIMIZATION_LEVEL[variant=Debug] "0") 30 | set_xcode_property(${TARGET} GCC_OPTIMIZATION_LEVEL[variant=Release] "3") 31 | 32 | # Architectures 33 | set_xcode_property(${TARGET} ARCHS "$(ARCHS_STANDARD)") 34 | set_xcode_property(${TARGET} ONLY_ACTIVE_ARCH[variant=Debug] "YES") 35 | set_xcode_property(${TARGET} ONLY_ACTIVE_ARCH[variant=Release] "NO") 36 | set_xcode_property(${TARGET} SDKROOT "macosx") 37 | 38 | # Debug Information 39 | set_xcode_property(${TARGET} DEBUG_INFORMATION_FORMAT[variant=Debug] "dwarf") 40 | set_xcode_property(${TARGET} DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym") 41 | set_xcode_property(${TARGET} GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Debug] "YES") 42 | set_xcode_property(${TARGET} GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES") 43 | set_xcode_property(${TARGET} GCC_NO_COMMON_BLOCKS "YES") 44 | 45 | # Common Warnings 46 | set_xcode_property(${TARGET} CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING "YES") 47 | set_xcode_property(${TARGET} CLANG_WARN_DOCUMENTATION_COMMENTS "YES") 48 | set_xcode_property(${TARGET} CLANG_WARN_EMPTY_BODY "YES") 49 | set_xcode_property(${TARGET} GCC_WARN_SHADOW "YES") ### 50 | set_xcode_property(${TARGET} CLANG_WARN_BOOL_CONVERSION "YES") 51 | set_xcode_property(${TARGET} CLANG_WARN_CONSTANT_CONVERSION "YES") 52 | set_xcode_property(${TARGET} GCC_WARN_64_TO_32_BIT_CONVERSION "YES") 53 | set_xcode_property(${TARGET} CLANG_WARN_ENUM_CONVERSION "YES") 54 | set_xcode_property(${TARGET} CLANG_WARN_FLOAT_CONVERSION "YES") ### 55 | set_xcode_property(${TARGET} CLANG_WARN_INT_CONVERSION "YES") 56 | set_xcode_property(${TARGET} CLANG_WARN_NON_LITERAL_NULL_CONVERSION "YES") 57 | set_xcode_property(${TARGET} CLANG_WARN_INFINITE_RECURSION "YES") 58 | set_xcode_property(${TARGET} GCC_WARN_ABOUT_RETURN_TYPE "YES_ERROR") 59 | set_xcode_property(${TARGET} GCC_WARN_ABOUT_MISSING_NEWLINE "YES") ### 60 | set_xcode_property(${TARGET} CLANG_WARN_ASSIGN_ENUM "YES") ### 61 | set_xcode_property(${TARGET} CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER "YES") 62 | set_xcode_property(${TARGET} GCC_WARN_SIGN_COMPARE "YES") ### 63 | set_xcode_property(${TARGET} CLANG_WARN_STRICT_PROTOTYPES "YES") 64 | set_xcode_property(${TARGET} CLANG_WARN_COMMA "YES") 65 | set_xcode_property(${TARGET} CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION "YES") ### 66 | set_xcode_property(${TARGET} CLANG_WARN_UNGUARDED_AVAILABILITY "YES_AGGRESSIVE") 67 | set_xcode_property(${TARGET} GCC_WARN_UNINITIALIZED_AUTOS "YES_AGGRESSIVE") 68 | set_xcode_property(${TARGET} CLANG_WARN_UNREACHABLE_CODE "YES") 69 | set_xcode_property(${TARGET} GCC_WARN_UNUSED_FUNCTION "YES") 70 | set_xcode_property(${TARGET} GCC_WARN_UNUSED_VALUE "YES") 71 | set_xcode_property(${TARGET} GCC_WARN_UNUSED_VARIABLE "YES") ### 72 | 73 | # C++ Warnings 74 | set_xcode_property(${TARGET} CLANG_WARN_RANGE_LOOP_ANALYSIS "YES") 75 | set_xcode_property(${TARGET} CLANG_WARN_SUSPICIOUS_MOVE "YES") 76 | 77 | # ObjC Warnings 78 | set_xcode_property(${TARGET} CLANG_WARN_DIRECT_OBJC_ISA_USAGE "YES_ERROR") 79 | set_xcode_property(${TARGET} CLANG_WARN__DUPLICATE_METHOD_MATCH "YES") 80 | set_xcode_property(${TARGET} CLANG_WARN_OBJC_LITERAL_CONVERSION "YES") 81 | set_xcode_property(${TARGET} CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS "YES") 82 | set_xcode_property(${TARGET} GCC_WARN_UNDECLARED_SELECTOR "YES") 83 | set_xcode_property(${TARGET} CLANG_WARN_OBJC_ROOT_CLASS "YES_ERROR") 84 | set_xcode_property(${TARGET} CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF "YES") 85 | 86 | # ObjC Options 87 | set_xcode_property(${TARGET} CLANG_ENABLE_OBJC_ARC "YES") 88 | set_xcode_property(${TARGET} CLANG_ENABLE_OBJC_WEAK "YES") 89 | set_xcode_property(${TARGET} ENABLE_NS_ASSERTIONS[variant=Debug] "YES") 90 | set_xcode_property(${TARGET} ENABLE_NS_ASSERTIONS[variant=Release] "NO") 91 | 92 | endmacro(set_default_xcode_property) 93 | 94 | 95 | # Set Xcode deployment version (macOS, iOS, tvOS, watchOS) 96 | # For example: set_xcode_deployment_version(some_target "10.11" "9.0" "9.0" "2.0") 97 | macro(set_xcode_deployment_version TARGET macOS iOS tvOS watchOS) 98 | set_xcode_property(${TARGET} MACOSX_DEPLOYMENT_TARGET ${macOS}) 99 | set_xcode_property(${TARGET} IPHONEOS_DEPLOYMENT_TARGET ${iOS}) 100 | set_xcode_property(${TARGET} TVOS_DEPLOYMENT_TARGET ${tvOS}) 101 | set_xcode_property(${TARGET} WATCHOS_DEPLOYMENT_TARGET ${watchOS}) 102 | endmacro(set_xcode_deployment_version) 103 | 104 | 105 | # Set Xcode language standard (C, CXX) 106 | # For example: set_xcode_language_standard(some_target "gnu11" "gnu++17") 107 | macro(set_xcode_language_standard TARGET C CXX) 108 | set_xcode_property(${TARGET} GCC_C_LANGUAGE_STANDARD ${C}) 109 | set_xcode_property(${TARGET} CLANG_CXX_LANGUAGE_STANDARD ${CXX}) 110 | endmacro(set_xcode_language_standard) 111 | -------------------------------------------------------------------------------- /src/yybench.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_h 7 | #define yybench_h 8 | 9 | #include "yybench_def.h" 10 | #include "yybench_cpu.h" 11 | #include "yybench_env.h" 12 | #include "yybench_str.h" 13 | #include "yybench_time.h" 14 | #include "yybench_file.h" 15 | #include "yybench_rand.h" 16 | #include "yybench_perf.h" 17 | #include "yybench_chart.h" 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/yybench_chart.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench_chart.h" 7 | #include "yybench_env.h" 8 | #include "yybench_cpu.h" 9 | #include "yybench_file.h" 10 | 11 | #define ARR_TYPE(type) yy_buf 12 | #define ARR_INIT(arr) yy_buf_init(&(arr), 0) 13 | #define ARR_RELEASE(arr) yy_buf_release(&(arr)) 14 | #define ARR_HEAD(arr, type) (((type *)(&(arr))->hdr)) 15 | #define ARR_GET(arr, type, idx) (((type *)(&(arr))->hdr) + idx) 16 | #define ARR_COUNT(arr, type) (yy_buf_len(&(arr)) / sizeof(type)) 17 | #define ARR_ADD(arr, value, type) (yy_buf_append(&(arr), (void *)&(value), sizeof(type))) 18 | 19 | typedef struct { 20 | f64 v; 21 | bool is_integer; 22 | bool is_null; 23 | } yy_chart_value; 24 | 25 | typedef struct { 26 | const char *name; 27 | ARR_TYPE(yy_chart_value) values; 28 | f64 avg_value; 29 | } yy_chart_item; 30 | 31 | struct yy_chart { 32 | yy_chart_options options; 33 | ARR_TYPE(yy_chart_item) items; 34 | bool item_opened; 35 | bool options_need_release; 36 | int ref_count; 37 | }; 38 | 39 | void yy_chart_options_init(yy_chart_options *op) { 40 | if (!op) return; 41 | memset(op, 0, sizeof(yy_chart_options)); 42 | op->type = YY_CHART_LINE; 43 | op->width = 800; 44 | op->height = 500; 45 | 46 | op->h_axis.min = op->h_axis.max = op->h_axis.tick_interval = NAN; 47 | op->h_axis.allow_decimals = true; 48 | op->v_axis.min = op->v_axis.max = op->v_axis.tick_interval = NAN; 49 | op->v_axis.allow_decimals = true; 50 | 51 | op->tooltip.value_decimals = -1; 52 | 53 | op->legend.enabled = true; 54 | op->legend.layout = YY_CHART_VERTICAL; 55 | op->legend.h_align = YY_CHART_RIGHT; 56 | op->legend.v_align = YY_CHART_MIDDLE; 57 | 58 | op->plot.point_interval = 1; 59 | op->plot.group_padding = 0.2f; 60 | op->plot.point_padding = 0.1f; 61 | op->plot.border_width = 1.0f; 62 | op->plot.value_labels_decimals = -1; 63 | } 64 | 65 | static int yy_chart_axis_category_count(yy_chart_axis_options *op) { 66 | int i; 67 | if (!op || !op->categories) return 0; 68 | for (i = 0; op->categories[i]; i++) {} 69 | return i; 70 | } 71 | 72 | static void yy_chart_axis_options_release(yy_chart_axis_options *op) { 73 | if (!op) return; 74 | if (op->title) free((void *)op->title); 75 | if (op->label_prefix) free((void *)op->label_prefix); 76 | if (op->label_suffix) free((void *)op->label_suffix); 77 | if (op->categories) { 78 | int i; 79 | for (i = 0; op->categories[i]; i++) { 80 | free((void *)op->categories[i]); 81 | } 82 | free((void *)op->categories); 83 | } 84 | } 85 | 86 | static bool yy_chart_axis_options_copy(yy_chart_axis_options *dst, 87 | yy_chart_axis_options *src) { 88 | #define RETURN_FAIL() do { yy_chart_axis_options_release(dst); return false;} while (0) 89 | #define STR_COPY(name) do { \ 90 | if (src->name) { \ 91 | dst->name = yy_str_copy(src->name); \ 92 | if (!(dst->name)) RETURN_FAIL(); \ 93 | } } while(0) 94 | if (!src || !dst) return false; 95 | STR_COPY(title); 96 | STR_COPY(label_prefix); 97 | STR_COPY(label_suffix); 98 | if (src->categories) { 99 | int i, count; 100 | count = yy_chart_axis_category_count(src); 101 | if (count) { 102 | dst->categories = calloc(count + 1, sizeof(char *)); 103 | if (!dst->categories) RETURN_FAIL(); 104 | for (i = 0 ; i < count; i++) STR_COPY(categories[i]); 105 | } 106 | } 107 | return true; 108 | #undef STR_COPY 109 | #undef RETURN_FAIL 110 | } 111 | 112 | static void yy_chart_options_release(yy_chart_options *op) { 113 | #define STR_FREE(name) if (op->name) free((void *)op->name) 114 | if (!op) return; 115 | STR_FREE(title); 116 | STR_FREE(subtitle); 117 | STR_FREE(tooltip.value_prefix); 118 | STR_FREE(tooltip.value_suffix); 119 | yy_chart_axis_options_release(&op->h_axis); 120 | yy_chart_axis_options_release(&op->v_axis); 121 | #undef STR_FREE 122 | } 123 | 124 | static bool yy_chart_options_copy(yy_chart_options *dst, 125 | yy_chart_options *src) { 126 | #define RETURN_FAIL() do { yy_chart_options_release(dst); return false;} while (0) 127 | #define STR_COPY(name) do { \ 128 | if (src->name) { \ 129 | dst->name = yy_str_copy(src->name); \ 130 | if (!(dst->name)) RETURN_FAIL(); \ 131 | } } while(0) 132 | if (!src || !dst) return false; 133 | *dst = *src; 134 | STR_COPY(title); 135 | STR_COPY(subtitle); 136 | STR_COPY(tooltip.value_prefix); 137 | STR_COPY(tooltip.value_suffix); 138 | if (!yy_chart_axis_options_copy(&dst->h_axis, &src->h_axis)) RETURN_FAIL(); 139 | if (!yy_chart_axis_options_copy(&dst->v_axis, &src->v_axis)) RETURN_FAIL(); 140 | return true; 141 | #undef STR_COPY 142 | #undef RETURN_FAIL 143 | } 144 | 145 | static bool yy_chart_item_release(yy_chart_item *item) { 146 | if (!item) return false; 147 | if (item->name) free((void *)item->name); 148 | ARR_RELEASE(item->values); 149 | return true; 150 | } 151 | 152 | static bool yy_chart_item_init(yy_chart_item *item, const char *name) { 153 | if (!item || !name) return false; 154 | memset(item, 0, sizeof(yy_chart_item)); 155 | if (!ARR_INIT(item->values)) return false; 156 | item->name = yy_str_copy(name); 157 | if (!item->name) { 158 | ARR_RELEASE(item->values); 159 | return false; 160 | } 161 | return true; 162 | } 163 | 164 | yy_chart *yy_chart_new(void) { 165 | yy_chart *chart = calloc(1, sizeof(struct yy_chart)); 166 | if (!chart) return NULL; 167 | yy_chart_options_init(&chart->options); 168 | chart->ref_count = 1; 169 | return chart; 170 | } 171 | 172 | void yy_chart_free(yy_chart *chart) { 173 | if (!chart) return; 174 | chart->ref_count--; 175 | if (chart->ref_count > 0) return; 176 | if (chart->options_need_release) yy_chart_options_release(&chart->options); 177 | free((void *)chart); 178 | } 179 | 180 | bool yy_chart_set_options(yy_chart *chart, yy_chart_options *op) { 181 | if (!chart || !op) return false; 182 | if (chart->options_need_release) { 183 | yy_chart_options_release(&chart->options); 184 | } 185 | if (yy_chart_options_copy(&chart->options, op)) { 186 | chart->options_need_release = true; 187 | return true; 188 | } else { 189 | yy_chart_options_init(&chart->options); 190 | chart->options_need_release = false; 191 | return false; 192 | } 193 | } 194 | 195 | bool yy_chart_item_begin(yy_chart *chart, const char *name) { 196 | yy_chart_item item; 197 | 198 | if (!chart || !name) return false; 199 | if (chart->item_opened) return false; 200 | 201 | if (!yy_chart_item_init(&item, name)) return false; 202 | if (!ARR_ADD(chart->items, item, yy_chart_item)) { 203 | yy_chart_item_release(&item); 204 | return false; 205 | } 206 | chart->item_opened = true; 207 | return true; 208 | } 209 | 210 | bool yy_chart_item_add_int(yy_chart *chart, int value) { 211 | size_t count; 212 | yy_chart_item *item; 213 | yy_chart_value cvalue; 214 | 215 | if (!chart || !chart->item_opened) return false; 216 | cvalue.v = value; 217 | cvalue.is_integer = true; 218 | cvalue.is_null = false; 219 | count = ARR_COUNT(chart->items, yy_chart_item); 220 | item = ARR_GET(chart->items, yy_chart_item, count - 1); 221 | return ARR_ADD(item->values, cvalue, yy_chart_value); 222 | } 223 | 224 | bool yy_chart_item_add_float(yy_chart *chart, float value) { 225 | size_t count; 226 | yy_chart_item *item; 227 | yy_chart_value cvalue; 228 | 229 | if (!chart || !chart->item_opened) return false; 230 | cvalue.v = value; 231 | cvalue.is_integer = false; 232 | cvalue.is_null = !isfinite(cvalue.v); 233 | count = ARR_COUNT(chart->items, yy_chart_item); 234 | item = ARR_GET(chart->items, yy_chart_item, count - 1); 235 | return ARR_ADD(item->values, cvalue, yy_chart_value); 236 | } 237 | 238 | bool yy_chart_item_end(yy_chart *chart) { 239 | if (!chart) return false; 240 | if (!chart->item_opened) return false; 241 | chart->item_opened = false; 242 | return true; 243 | } 244 | 245 | bool yy_chart_item_with_int(yy_chart *chart, const char *name, int value) { 246 | if (!yy_chart_item_begin(chart, name) || 247 | !yy_chart_item_add_int(chart, value) || 248 | !yy_chart_item_end(chart)) return false; 249 | return true; 250 | } 251 | 252 | bool yy_chart_item_with_float(yy_chart *chart, const char *name, float value) { 253 | if (!yy_chart_item_begin(chart, name) || 254 | !yy_chart_item_add_float(chart, value) || 255 | !yy_chart_item_end(chart)) return false; 256 | return true; 257 | } 258 | 259 | static int yy_chart_item_cmp_value_asc(const void *p1, const void *p2) { 260 | f64 v1, v2; 261 | v1 = ((yy_chart_item *)p1)->avg_value; 262 | v2 = ((yy_chart_item *)p2)->avg_value; 263 | if (v1 == v2) return 0; 264 | return v1 < v2 ? -1 : 1; 265 | } 266 | 267 | static int yy_chart_item_cmp_value_desc(const void *p1, const void *p2) { 268 | f64 v1, v2; 269 | v1 = ((yy_chart_item *)p1)->avg_value; 270 | v2 = ((yy_chart_item *)p2)->avg_value; 271 | if (v1 == v2) return 0; 272 | return v1 > v2 ? -1 : 1; 273 | } 274 | 275 | bool yy_chart_sort_items_with_value(yy_chart *chart, bool ascent) { 276 | size_t i, imax, j, jmax, count; 277 | f64 sum; 278 | 279 | for ((void)(i = 0), imax = ARR_COUNT(chart->items, yy_chart_item); i < imax; i++) { 280 | yy_chart_item *item = ARR_GET(chart->items, yy_chart_item, i); 281 | count = 0; 282 | sum = 0; 283 | for ((void)(j = 0), jmax = ARR_COUNT(item->values, yy_chart_value); j < jmax; j++) { 284 | yy_chart_value *value = ARR_GET(item->values, yy_chart_value, j); 285 | if (!value->is_null) { 286 | count++; 287 | sum += value->v; 288 | } 289 | } 290 | item->avg_value = count ? sum / count : 0; 291 | } 292 | 293 | if (imax <= 1) return true; 294 | qsort(chart->items.hdr, imax, sizeof(yy_chart_item), 295 | ascent ? yy_chart_item_cmp_value_asc : yy_chart_item_cmp_value_desc); 296 | return true; 297 | } 298 | 299 | static int yy_chart_item_cmp_name_asc(const void *p1, const void *p2) { 300 | return strcmp(((yy_chart_item *)p1)->name, ((yy_chart_item *)p2)->name); 301 | } 302 | 303 | static int yy_chart_item_cmp_name_desc(const void *p1, const void *p2) { 304 | return -strcmp(((yy_chart_item *)p1)->name, ((yy_chart_item *)p2)->name); 305 | } 306 | 307 | bool yy_chart_sort_items_with_name(yy_chart *chart, bool ascent) { 308 | size_t count; 309 | 310 | if (!chart) return false; 311 | count = ARR_COUNT(chart->items, yy_chart_item); 312 | if (count <= 1) return true; 313 | qsort(chart->items.hdr, count, sizeof(yy_chart_item), 314 | ascent ? yy_chart_item_cmp_name_asc : yy_chart_item_cmp_name_desc); 315 | return true; 316 | } 317 | 318 | 319 | 320 | struct yy_report { 321 | ARR_TYPE(yy_chart *) charts; 322 | ARR_TYPE(char *) infos; 323 | }; 324 | 325 | yy_report *yy_report_new(void) { 326 | yy_report *report = calloc(1, sizeof(yy_report)); 327 | return report; 328 | } 329 | 330 | void yy_report_free(yy_report *report) { 331 | if (!report) return; 332 | usize i, count; 333 | 334 | count = ARR_COUNT(report->charts, yy_chart); 335 | for (i = 0; i < count; i++) { 336 | yy_chart_free(*ARR_GET(report->charts, yy_chart *, i)); 337 | } 338 | 339 | count = ARR_COUNT(report->infos, char *); 340 | for (i = 0; i < count; i++) { 341 | free(*ARR_GET(report->infos, char *, i)); 342 | } 343 | } 344 | 345 | bool yy_report_add_chart(yy_report *report, yy_chart *chart) { 346 | if (!report || !chart) return false; 347 | if (ARR_ADD(report->charts, chart, yy_chart *)) { 348 | chart->ref_count++; 349 | return true; 350 | } 351 | return false; 352 | } 353 | 354 | bool yy_report_add_info(yy_report *report, const char *info) { 355 | if (!report || !info) return false; 356 | char *str = yy_str_copy(info); 357 | if (!str) return false; 358 | if (ARR_ADD(report->infos, str, char *)) { 359 | return true; 360 | } else { 361 | free(str); 362 | return false; 363 | } 364 | } 365 | 366 | bool yy_report_add_env_info(yy_report *report) { 367 | char info[1024]; 368 | snprintf(info, sizeof(info), "Compiler: %s", yy_env_get_compiler_desc()); 369 | if (!yy_report_add_info(report, info)) return false; 370 | snprintf(info, sizeof(info), "OS: %s", yy_env_get_os_desc()); 371 | if (!yy_report_add_info(report, info)) return false; 372 | snprintf(info, sizeof(info), "CPU: %s", yy_env_get_cpu_desc()); 373 | if (!yy_report_add_info(report, info)) return false; 374 | snprintf(info, sizeof(info), "CPU Frequency: %.2f MHz", yy_cpu_get_freq() / 1000.0 / 1000.0); 375 | if (!yy_report_add_info(report, info)) return false; 376 | return true; 377 | } 378 | 379 | bool yy_report_write_html_string(yy_report *report, char **html, usize *len) { 380 | /* append line string */ 381 | #define LS(str) do { \ 382 | if (!yy_sb_append(sb, str)) goto fail; \ 383 | if (!yy_sb_append(sb, "\n")) goto fail; } while(0) 384 | /* append line format */ 385 | #define LF(str, arg) do { \ 386 | if (!yy_sb_printf(sb, str, arg)) goto fail; \ 387 | if (!yy_sb_append(sb, "\n")) goto fail; } while(0) 388 | /* append string */ 389 | #define AS(str) do { if (!yy_sb_append(sb, str)) goto fail; } while(0) 390 | /* append string format */ 391 | #define AF(str, arg) do { if (!yy_sb_printf(sb, str, arg)) goto fail; } while(0) 392 | /* append string escaped (single quote) */ 393 | #define AE(str) do { if (!yy_sb_append_esc(sb, '\'', str)) goto fail; } while(0) 394 | /* append string escaped (html) */ 395 | #define AH(str) do { if (!yy_sb_append_html(sb, str)) goto fail; } while(0) 396 | /* string with default value */ 397 | #define STRDEF(str, def) ((str) ? (str) : (def)) 398 | /* string with bool */ 399 | #define STRBOOL(flag) ((flag) ? "true" : "false") 400 | 401 | yy_chart_options *op; 402 | yy_chart_axis_options *x_axis; 403 | yy_chart_axis_options *y_axis; 404 | yy_chart_item *item; 405 | yy_chart_value *val; 406 | yy_sb _sb; 407 | yy_sb *sb; 408 | const char *str; 409 | const char **str_arr; 410 | usize i, c, v, val_count, cate_count, max_count; 411 | 412 | usize chart_count = ARR_COUNT(report->charts, yy_chart *); 413 | usize info_count = ARR_COUNT(report->infos, char *); 414 | 415 | if (len) *len = 0; 416 | if (!report || !html) return false; 417 | if (!yy_sb_init(&_sb, 0)) return false; 418 | sb = &_sb; 419 | 420 | LS(""); 421 | LS(""); 422 | LS(""); 423 | LS(""); 424 | LS("Report"); 425 | LS(""); 426 | LS(""); 427 | LS(""); 428 | LS(""); 429 | LS(""); 430 | LS(""); 431 | LS(""); 432 | LS(""); 433 | LS(""); 434 | LS(""); 468 | LS(""); 469 | LS(""); 470 | LS(""); 471 | LS(""); 508 | 509 | for (c = 0; c < chart_count; c++) { 510 | yy_chart *chart = *ARR_GET(report->charts, yy_chart *, c); 511 | usize item_count = ARR_COUNT(chart->items, yy_chart_item); 512 | 513 | op = &chart->options; 514 | 515 | // simple html table, not chart 516 | if (op->type == YY_CHART_TABLE) { 517 | LS(""); 518 | LF("", c); 519 | LS("
"); 520 | AF("
"); 523 | LS(" "); 524 | 525 | // header 526 | AS(" "); 527 | LS(" "); 528 | AS(" "); 529 | AS(""); 530 | if (op->h_axis.categories && op->h_axis.categories[0]) { 531 | for(str_arr = op->h_axis.categories; str_arr[0]; str_arr++) { 532 | AS(""); 533 | } 534 | } 535 | LS(""); 536 | LS(" "); 537 | 538 | // body 539 | LS(" "); 540 | 541 | for (i = 0; i < item_count; i++) { 542 | item = ARR_GET(chart->items, yy_chart_item, i); 543 | AS(" "); 544 | AS(""); 545 | for ((void)(v = 0), val_count = ARR_COUNT(item->values, yy_chart_value); v < val_count; v++) { 546 | val = ARR_GET(item->values, yy_chart_value, v); 547 | AS(""); 552 | } 553 | LS(""); 554 | } 555 | 556 | LS(" "); 557 | 558 | LS("
"); AE(STRDEF(op->title, "Unnamed Table")); LS("
Name"); AE(STRDEF(str_arr[0], "null")); AS("
"); AE(item->name); AS(""); 548 | if (val->is_null) AS("null"); 549 | else if (val->is_integer) AF("%d", (int)val->v); 550 | else AF("%f", (float)val->v); 551 | AS("
"); 559 | LS("
"); 560 | continue; 561 | } 562 | 563 | 564 | if (op->type == YY_CHART_BAR) { 565 | x_axis = &op->v_axis; 566 | y_axis = &op->h_axis; 567 | } else { 568 | x_axis = &op->h_axis; 569 | y_axis = &op->v_axis; 570 | } 571 | 572 | LS(""); 573 | LF("", c); 574 | LS("
"); 575 | AF("
"); 579 | LS(""); 781 | } 782 | 783 | LS(""); 784 | LS(""); 785 | AS(""); 786 | 787 | *html = yy_sb_get_str(sb); 788 | if (!*html) goto fail; 789 | if (len) *len = yy_sb_get_len(sb); 790 | return true; 791 | 792 | fail: 793 | yy_sb_release(sb); 794 | return false; 795 | } 796 | 797 | bool yy_report_write_html_file(yy_report *report, const char *path) { 798 | char *html; 799 | usize len; 800 | if (!yy_report_write_html_string(report, &html, &len)) return false; 801 | bool suc = yy_file_write(path, (u8 *)html, len); 802 | free(html); 803 | return suc; 804 | } 805 | -------------------------------------------------------------------------------- /src/yybench_chart.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_chart_h 7 | #define yybench_chart_h 8 | 9 | #include "yybench_def.h" 10 | #include "yybench_str.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | 17 | /*============================================================================== 18 | * Chart 19 | *============================================================================*/ 20 | 21 | /* 22 | It use Highcharts (Not-for-Profit usage license) to display charts. 23 | If you need to use it commercially, you need to obtain a new license, 24 | or you can change it with another opensource chart library. 25 | 26 | Code Example: 27 | 28 | // Create a report, add some infos. 29 | yy_report *report = yy_report_new(); 30 | yy_report_add_info(report, "This is a report demo"); 31 | yy_report_add_info(report, "The chart is rendered with highcharts"); 32 | 33 | { 34 | // Config line chart options. 35 | yy_chart_options op; 36 | yy_chart_options_init(&op); 37 | op.title = "Line Chart Demo"; 38 | op.type = YY_CHART_LINE; 39 | op.v_axis.title = "this is v axis"; 40 | op.h_axis.title = "this is h axis"; 41 | op.tooltip.value_decimals = 3; 42 | 43 | // Create a chart and set options. 44 | yy_chart *chart = yy_chart_new(); 45 | yy_chart_set_options(chart, &op); 46 | 47 | // Add a line to chart. 48 | yy_chart_item_begin(chart, "sin line"); 49 | for (float i = 0; i < M_PI * 2; i += 0.1f) { 50 | yy_chart_item_add_float(chart, sinf(i)); 51 | } 52 | yy_chart_item_end(chart); 53 | 54 | // Add a line to chart. 55 | yy_chart_item_begin(chart, "cos line"); 56 | for (float i = 0; i < M_PI * 2; i += 0.1f) { 57 | yy_chart_item_add_float(chart, cosf(i)); 58 | } 59 | yy_chart_item_end(chart); 60 | 61 | // Add chart to report, and free the chart. 62 | yy_report_add_chart(report, chart); 63 | yy_chart_free(chart); 64 | } 65 | 66 | { 67 | // Config bar chart options. 68 | yy_chart_options op; 69 | yy_chart_options_init(&op); 70 | op.title = "Bar Chart Demo"; 71 | op.type = YY_CHART_BAR; 72 | op.v_axis.title = "this is v axis"; 73 | op.h_axis.title = "this is h axis"; 74 | const char *categories[] = {"Q1", "Q2", "Q3", "Q4", NULL};; 75 | op.v_axis.categories = categories; 76 | 77 | // Create a chart and set options. 78 | yy_chart *chart = yy_chart_new(); 79 | yy_chart_set_options(chart, &op); 80 | 81 | // Add a bar group to chart. 82 | yy_chart_item_begin(chart, "year 2019"); 83 | yy_chart_item_add_int(chart, 20); 84 | yy_chart_item_add_int(chart, 25); 85 | yy_chart_item_add_int(chart, 30); 86 | yy_chart_item_add_int(chart, 15); 87 | yy_chart_item_end(chart); 88 | 89 | // Add a bar group to chart. 90 | yy_chart_item_begin(chart, "year 2020"); 91 | yy_chart_item_add_int(chart, 20); 92 | yy_chart_item_add_int(chart, 30); 93 | yy_chart_item_add_int(chart, 45); 94 | yy_chart_item_add_int(chart, 25); 95 | yy_chart_item_end(chart); 96 | 97 | // Add chart to report, and free the chart. 98 | yy_report_add_chart(report, chart); 99 | yy_chart_free(chart); 100 | 101 | } 102 | 103 | // Write and free the report 104 | yy_report_write_html_file(report, "report.html"); 105 | yy_report_free(report); 106 | */ 107 | 108 | 109 | /** Chart options enum */ 110 | typedef enum { 111 | /* Chart type */ 112 | YY_CHART_LINE, /* line chart */ 113 | YY_CHART_BAR, /* bar chart */ 114 | YY_CHART_COLUMN, /* column chart */ 115 | YY_CHART_PIE, /* pie chart */ 116 | YY_CHART_TABLE, /* table (not a chart) */ 117 | 118 | /* Legend layout */ 119 | YY_CHART_HORIZONTAL, /* horizontal layout */ 120 | YY_CHART_VERTICAL, /* vertical layout */ 121 | YY_CHART_PROXIMATE, /* close to the graphs they're representing */ 122 | 123 | /* Horizontal alignment */ 124 | YY_CHART_LEFT, /* align to left */ 125 | YY_CHART_CENTER, /* align to center */ 126 | YY_CHART_RIGHT, /* align to right */ 127 | 128 | /* Vertical alignment */ 129 | YY_CHART_TOP, /* align to top */ 130 | YY_CHART_MIDDLE, /* align to middle */ 131 | YY_CHART_BOTTOM /* align to bottom */ 132 | } yy_chart_enum; 133 | 134 | /** Chart axis options */ 135 | typedef struct { 136 | const char *title; /* axis title, default is NULL */ 137 | const char *label_prefix; /* a string to prepend to each axis label, default is NULL */ 138 | const char *label_suffix; /* a string to append to each axis label, default is NULL */ 139 | float min, max; /* the min/max value of the axis, default is NaN */ 140 | float tick_interval; /* the interval of the tick marks in axis units, default is NaN */ 141 | bool allow_decimals; /* whether to allow decimals in this axis' ticks, default is true */ 142 | bool logarithmic; /* type of axis (linear or logarithmic), default is false (linear) */ 143 | const char **categories; /* If categories are present for this axis, names are used instead of 144 | numbers for this axis. For example: {"Q1", "Q2", "Q3", "Q4", NULL}; */ 145 | } yy_chart_axis_options; 146 | 147 | /** Chart tooltip options */ 148 | typedef struct { 149 | int value_decimals; /* how many decimals to show in each value, default is -1 (preserve all decimals) */ 150 | const char *value_prefix; /* a string to prepend to each series' value, default is NULL */ 151 | const char *value_suffix; /* a string to append to each series' value, default is NULL */ 152 | bool shared; /* the entire plot area will capture mouse movement, default is false */ 153 | bool crosshairs; /* enable a crosshair for the value, default is false */ 154 | } yy_chart_tooltip_options; 155 | 156 | /** Chart legend options */ 157 | typedef struct { 158 | bool enabled; /* enable or disable the legend, default is true */ 159 | yy_chart_enum layout; /* the layout of the legend items, default is vertical */ 160 | yy_chart_enum h_align; /* the horizontal alignment of the legend box, default is right */ 161 | yy_chart_enum v_align; /* the vertical alignment of the legend box, default is middle */ 162 | } yy_chart_legend_options; 163 | 164 | /** Chart plot options */ 165 | typedef struct { 166 | bool name_label_enabled; /* enable the name label per item, default is false */ 167 | bool value_labels_enabled; /* enable all value labels, default is false */ 168 | int value_labels_decimals; /* how many decimals to show in each value, default is -1 */ 169 | /* Options for line chart */ 170 | float point_start; /* the start of the x values, default is 0 */ 171 | float point_interval; /* the interval of the x values, default is 1 */ 172 | /* Options for bar and column chart */ 173 | bool color_by_point; /* one color per item (group), default is false */ 174 | bool group_stacked; /* stack the values of each item (group), default is false */ 175 | float group_padding; /* padding between each item (group), default is 0.2 */ 176 | float point_padding; /* padding between each column or bar, default is 0.1 */ 177 | float border_width; /* the width of the border surrounding each column or bar, default is 1 */ 178 | } yy_chart_plot_options; 179 | 180 | /** Chart options */ 181 | typedef struct { 182 | yy_chart_enum type; /* chart type, default is 'line' */ 183 | int width, height; /* chart size, default is 800*500 */ 184 | const char *title; /* title for chart */ 185 | const char *subtitle; /* subtitle for chart */ 186 | const char **colors; /* color pattern, for example {"#058DC7", "#50B432", "#ED561B", "#DDDF00", NULL} */ 187 | yy_chart_axis_options v_axis; /* vertical axis options */ 188 | yy_chart_axis_options h_axis; /* horizontal axis options */ 189 | yy_chart_tooltip_options tooltip; /* tooltip options */ 190 | yy_chart_legend_options legend; /* legend options */ 191 | yy_chart_plot_options plot; /* plot options */ 192 | } yy_chart_options; 193 | 194 | /** Set chart options to default value */ 195 | void yy_chart_options_init(yy_chart_options *op); 196 | 197 | 198 | /** A chart object */ 199 | typedef struct yy_chart yy_chart; 200 | 201 | /** Create a new chart object. */ 202 | yy_chart *yy_chart_new(void); 203 | 204 | /** Release the chart object. */ 205 | void yy_chart_free(yy_chart *chart); 206 | 207 | /** Set chart options, the options was copied. */ 208 | bool yy_chart_set_options(yy_chart *chart, yy_chart_options *op); 209 | 210 | /** Begin a chart item */ 211 | bool yy_chart_item_begin(yy_chart *chart, const char *name); 212 | 213 | /** Add an integer value to current chart item */ 214 | bool yy_chart_item_add_int(yy_chart *chart, int value); 215 | 216 | /** Add a floating value to current chart item */ 217 | bool yy_chart_item_add_float(yy_chart *chart, float value); 218 | 219 | /** End a chart item */ 220 | bool yy_chart_item_end(yy_chart *chart); 221 | 222 | /** Same as item_begin(name); item_add_int(value), item_end(); */ 223 | bool yy_chart_item_with_int(yy_chart *chart, const char *name, int value); 224 | 225 | /** Same as item_begin(name); item_add_float(value), item_end(); */ 226 | bool yy_chart_item_with_float(yy_chart *chart, const char *name, float value); 227 | 228 | /** Sort items with average values (ascent or descent) */ 229 | bool yy_chart_sort_items_with_value(yy_chart *chart, bool ascent); 230 | 231 | /** Sort items with name (ascent or descent) */ 232 | bool yy_chart_sort_items_with_name(yy_chart *chart, bool ascent); 233 | 234 | 235 | /*============================================================================== 236 | * HTML Report 237 | *============================================================================*/ 238 | 239 | /** A report object */ 240 | typedef struct yy_report yy_report; 241 | 242 | /** Creates a report. */ 243 | yy_report *yy_report_new(void); 244 | 245 | /** Release a report. */ 246 | void yy_report_free(yy_report *report); 247 | 248 | /** Add a chart to report. */ 249 | bool yy_report_add_chart(yy_report *report, yy_chart *chart); 250 | 251 | /** Add a text information to report. */ 252 | bool yy_report_add_info(yy_report *report, const char *info); 253 | 254 | /** Add environment information to report. */ 255 | bool yy_report_add_env_info(yy_report *report); 256 | 257 | /** Write the report to html string, should be released with free(). */ 258 | bool yy_report_write_html_string(yy_report *report, char **html, usize *len); 259 | 260 | /** Write the report to html file. */ 261 | bool yy_report_write_html_file(yy_report *report, const char *path); 262 | 263 | 264 | #ifdef __cplusplus 265 | } 266 | #endif 267 | 268 | #endif 269 | -------------------------------------------------------------------------------- /src/yybench_cpu.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench_cpu.h" 7 | #include "yybench_time.h" 8 | 9 | #define REPEAT_2(x) x x 10 | #define REPEAT_4(x) REPEAT_2(REPEAT_2(x)) 11 | #define REPEAT_8(x) REPEAT_2(REPEAT_4(x)) 12 | #define REPEAT_16(x) REPEAT_2(REPEAT_8(x)) 13 | #define REPEAT_32(x) REPEAT_2(REPEAT_16(x)) 14 | #define REPEAT_64(x) REPEAT_2(REPEAT_32(x)) 15 | #define REPEAT_128(x) REPEAT_2(REPEAT_64(x)) 16 | #define REPEAT_256(x) REPEAT_2(REPEAT_128(x)) 17 | #define REPEAT_512(x) REPEAT_2(REPEAT_256(x)) 18 | 19 | 20 | bool yy_cpu_setup_priority(void) { 21 | #if defined(_WIN32) 22 | BOOL ret1 = SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); 23 | BOOL ret2 = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); 24 | return ret1 && ret2; 25 | #else 26 | int policy; 27 | struct sched_param param; 28 | pthread_t thread = pthread_self(); 29 | pthread_getschedparam(thread, &policy, ¶m); 30 | param.sched_priority = sched_get_priority_max(policy); 31 | if (param.sched_priority != -1) { 32 | return pthread_setschedparam(pthread_self(), policy, ¶m) == 0; 33 | } 34 | return false; 35 | #endif 36 | } 37 | 38 | void yy_cpu_spin(f64 second) { 39 | f64 end = yy_time_get_seconds() + second; 40 | while(yy_time_get_seconds() < end) { 41 | volatile int x = 0; 42 | while (x < 1000) x++; 43 | } 44 | } 45 | 46 | #if (yy_has_attribute(naked)) && YY_ARCH_ARM64 47 | #define YY_CPU_RUN_INST_COUNT_A (8192 * (128 + 256)) 48 | #define YY_CPU_RUN_INST_COUNT_B (8192 * (512)) 49 | 50 | __attribute__((naked, noinline)) 51 | void yy_cpu_run_seq_a(void) { 52 | __asm volatile 53 | ( 54 | "mov x0, #8192\n" 55 | "Loc_seq_loop_begin_a:\n" 56 | REPEAT_128("add x1, x1, x1\n") 57 | REPEAT_256("add x1, x1, x1\n") 58 | "subs x0, x0, #1\n" 59 | "bne Loc_seq_loop_begin_a\n" 60 | "ret\n" 61 | ); 62 | } 63 | 64 | __attribute__((naked, noinline)) 65 | void yy_cpu_run_seq_b(void) { 66 | __asm volatile 67 | ( 68 | "mov x0, #8192\n" 69 | "Loc_seq_loop_begin_b:\n" 70 | REPEAT_512("add x1, x1, x1\n") 71 | "subs x0, x0, #1\n" 72 | "bne Loc_seq_loop_begin_b\n" 73 | "ret\n" 74 | ); 75 | } 76 | 77 | #elif (yy_has_attribute(naked)) && YY_ARCH_ARM32 78 | #define YY_CPU_RUN_INST_COUNT_A (8192 * (128 + 256)) 79 | #define YY_CPU_RUN_INST_COUNT_B (8192 * (512)) 80 | 81 | __attribute__((naked, noinline)) 82 | void yy_cpu_run_seq_a(void) { 83 | __asm volatile 84 | ( 85 | "mov.w r0, #8192\n" 86 | "Loc_seq_loop_begin_a:\n" 87 | REPEAT_128("add r1, r1, r1\n") 88 | REPEAT_256("add r1, r1, r1\n") 89 | "subs r0, r0, #1\n" 90 | "bne.w Loc_seq_loop_begin_a\n" 91 | "bx lr\n" 92 | ); 93 | } 94 | 95 | __attribute__((naked, noinline)) 96 | void yy_cpu_run_seq_b(void) { 97 | __asm volatile 98 | ( 99 | "mov.w r0, #8192\n" 100 | "Loc_seq_loop_begin_b:\n" 101 | REPEAT_512("add r1, r1, r1\n") 102 | "subs r0, r0, #1\n" 103 | "bne.w Loc_seq_loop_begin_b\n" 104 | "bx lr\n" 105 | ); 106 | } 107 | 108 | #elif (yy_has_attribute(naked)) && (YY_ARCH_X64 || YY_ARCH_X86) 109 | #define YY_CPU_RUN_INST_COUNT_A (8192 * (128 + 256)) 110 | #define YY_CPU_RUN_INST_COUNT_B (8192 * (512)) 111 | 112 | __attribute__((naked, noinline)) 113 | void yy_cpu_run_seq_a(void) { 114 | __asm volatile 115 | ( 116 | "movl $8192, %eax\n" 117 | "seq_loop_begin_a:\n" 118 | REPEAT_128("addl %edx, %edx\n") 119 | REPEAT_256("addl %edx, %edx\n") 120 | "subl $1, %eax\n" 121 | "jne seq_loop_begin_a\n" 122 | "ret\n" 123 | ); 124 | } 125 | 126 | __attribute__((naked, noinline)) 127 | void yy_cpu_run_seq_b(void) { 128 | __asm volatile 129 | ( 130 | "movl $8192, %eax\n" 131 | "seq_loop_begin_b:\n" 132 | REPEAT_512("addl %edx, %edx\n") 133 | "subl $1, %eax\n" 134 | "jne seq_loop_begin_b\n" 135 | "ret\n" 136 | ); 137 | } 138 | 139 | #else 140 | #define YY_CPU_RUN_INST_COUNT_A (8192 * 4 * (32 + 64)) 141 | #define YY_CPU_RUN_INST_COUNT_B (8192 * 4 * (128)) 142 | 143 | /* These functions contains some `add` instructions with data dependence. 144 | This file should be compiled with optimization flag on. 145 | We hope that each line of the code in the inner loop may compiled as 146 | an `add` instruction, each `add` instruction takes 1 cycle, and inner kernel 147 | can fit in the L1i cache. Try: https://godbolt.org/z/d3GP1b */ 148 | 149 | u32 yy_cpu_run_seq_vals[8]; 150 | 151 | void yy_cpu_run_seq_a(void) { 152 | u32 loop = 8192; 153 | u32 v1 = yy_cpu_run_seq_vals[1]; 154 | u32 v2 = yy_cpu_run_seq_vals[2]; 155 | u32 v3 = yy_cpu_run_seq_vals[3]; 156 | u32 v4 = yy_cpu_run_seq_vals[4]; 157 | do { 158 | REPEAT_32( v1 += v4; v2 += v1; v3 += v2; v4 += v3; ) 159 | REPEAT_64( v1 += v4; v2 += v1; v3 += v2; v4 += v3; ) 160 | } while(--loop); 161 | yy_cpu_run_seq_vals[0] = v1; 162 | } 163 | 164 | void yy_cpu_run_seq_b(void) { 165 | u32 loop = 8192; 166 | u32 v1 = yy_cpu_run_seq_vals[1]; 167 | u32 v2 = yy_cpu_run_seq_vals[2]; 168 | u32 v3 = yy_cpu_run_seq_vals[3]; 169 | u32 v4 = yy_cpu_run_seq_vals[4]; 170 | do { 171 | REPEAT_128( v1 += v4; v2 += v1; v3 += v2; v4 += v3; ) 172 | } while(--loop); 173 | yy_cpu_run_seq_vals[0] = v1; 174 | } 175 | 176 | #endif 177 | 178 | static u64 yy_cycle_per_sec = 0; 179 | static u64 yy_tick_per_sec = 0; 180 | 181 | void yy_cpu_measure_freq(void) { 182 | #define warmup_count 8 183 | #define measure_count 128 184 | yy_time p1, p2; 185 | u64 ticks_a[measure_count]; 186 | u64 ticks_b[measure_count]; 187 | 188 | /* warm up CPU caches and stabilize the frequency */ 189 | for (int i = 0; i < warmup_count; i++) { 190 | yy_cpu_run_seq_a(); 191 | yy_cpu_run_seq_b(); 192 | yy_time_get_current(&p1); 193 | yy_time_get_ticks(); 194 | } 195 | 196 | /* run sequence a and b repeatedly, record ticks and times */ 197 | yy_time_get_current(&p1); 198 | u64 t1 = yy_time_get_ticks(); 199 | for (int i = 0; i < measure_count; i++) { 200 | u64 s1 = yy_time_get_ticks(); 201 | yy_cpu_run_seq_a(); 202 | u64 s2 = yy_time_get_ticks(); 203 | yy_cpu_run_seq_b(); 204 | u64 s3 = yy_time_get_ticks(); 205 | ticks_a[i] = s2 - s1; 206 | ticks_b[i] = s3 - s2; 207 | } 208 | u64 t2 = yy_time_get_ticks(); 209 | yy_time_get_current(&p2); 210 | 211 | /* calculate tick count per second, this value is high precision */ 212 | f64 total_seconds = yy_time_to_seconds(&p2) - yy_time_to_seconds(&p1); 213 | u64 total_ticks = t2 - t1; 214 | yy_tick_per_sec = (u64)((f64)total_ticks / total_seconds); 215 | 216 | /* find the minimum ticks of each sequence to avoid inaccurate values 217 | caused by context switching, etc. */ 218 | for (int i = 1; i < measure_count; i++) { 219 | if (ticks_a[i] < ticks_a[0]) ticks_a[0] = ticks_a[i]; 220 | if (ticks_b[i] < ticks_b[0]) ticks_b[0] = ticks_b[i]; 221 | } 222 | 223 | /* use the difference between two sequences to eliminate the overhead of 224 | loops and function calls */ 225 | u64 one_ticks = ticks_b[0] - ticks_a[0]; 226 | u64 one_insts = YY_CPU_RUN_INST_COUNT_B - YY_CPU_RUN_INST_COUNT_A; 227 | yy_cycle_per_sec = (u64)((f64)one_insts / (f64)one_ticks * (f64)yy_tick_per_sec); 228 | #undef warmup_count 229 | #undef measure_count 230 | } 231 | 232 | u64 yy_cpu_get_freq(void) { 233 | return yy_cycle_per_sec; 234 | } 235 | 236 | u64 yy_cpu_get_tick_per_sec(void) { 237 | return yy_tick_per_sec; 238 | } 239 | 240 | f64 yy_cpu_get_cycle_per_tick(void) { 241 | return (f64)yy_cycle_per_sec / (f64)yy_tick_per_sec; 242 | } 243 | 244 | f64 yy_cpu_tick_to_sec(u64 tick) { 245 | return tick / (f64)yy_tick_per_sec;; 246 | } 247 | 248 | u64 yy_cpu_tick_to_cycle(u64 tick) { 249 | return (u64)(tick * ((f64)yy_cycle_per_sec / (f64)yy_tick_per_sec)); 250 | } 251 | -------------------------------------------------------------------------------- /src/yybench_cpu.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_cpu_h 7 | #define yybench_cpu_h 8 | 9 | #include "yybench_def.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | 16 | /*============================================================================== 17 | * CPU Utils 18 | *============================================================================*/ 19 | 20 | /** Try to increase the priority of the current thread. 21 | This method may used to reduce context switches of current thread. */ 22 | bool yy_cpu_setup_priority(void); 23 | 24 | /** Let CPU 'spinning' for a while. 25 | This function may used to warm up CPU from power saving mode and 26 | stabilize the CPU frequency. */ 27 | void yy_cpu_spin(f64 second); 28 | 29 | /** Measure current CPU frequency. 30 | This function may take about 1 second on 1GHz CPU. 31 | This function may returns inaccurate result in debug mode. */ 32 | void yy_cpu_measure_freq(void); 33 | 34 | /** Returns CPU frequency in Hz. 35 | You should call yy_cpu_measure_freq() at least once before calling this 36 | function. */ 37 | u64 yy_cpu_get_freq(void); 38 | 39 | /** Returns tick per second. 40 | You should call yy_cpu_measure_freq() at least once before calling this 41 | function. This function may used with yy_time_get_ticks() for benchmark. */ 42 | u64 yy_cpu_get_tick_per_sec(void); 43 | 44 | /** Returns cpu cycles per tick. 45 | You should call yy_cpu_measure_freq() at least once before calling this 46 | function. This function may used with yy_time_get_ticks() for benchmark. */ 47 | f64 yy_cpu_get_cycle_per_tick(void); 48 | 49 | /** Convert tick to second. 50 | You should call yy_cpu_measure_freq() at least once before calling this 51 | function. This function may used with yy_time_get_ticks() for benchmark. */ 52 | f64 yy_cpu_tick_to_sec(u64 tick); 53 | 54 | /** Convert tick to CPU cycle. 55 | You should call yy_cpu_measure_freq() at least once before calling this 56 | function. This function may used with yy_time_get_ticks() for benchmark. */ 57 | u64 yy_cpu_tick_to_cycle(u64 tick); 58 | 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/yybench_def.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_def_h 7 | #define yybench_def_h 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | #ifdef _WIN32 22 | # include 23 | # include 24 | # include 25 | #else 26 | # ifdef __APPLE__ 27 | # include 28 | # include 29 | # include 30 | # include 31 | # include 32 | # include 33 | # include 34 | # include 35 | # else 36 | # ifndef _GNU_SOURCE 37 | # define _GNU_SOURCE 38 | # endif 39 | # ifndef __USE_GNU 40 | # define __USE_GNU 41 | # endif 42 | # endif 43 | # include 44 | # include 45 | # include 46 | # include 47 | # include 48 | #endif 49 | 50 | /* architecture */ 51 | #if defined(__x86_64) || defined(__x86_64__) || \ 52 | defined(__amd64) || defined(__amd64__) || \ 53 | defined(_M_AMD64) || defined(_M_X64) 54 | # define YY_ARCH_X64 1 55 | # define YY_ARCH_64 1 56 | #elif defined(i386) || defined(__i386) || defined(__i386__) || \ 57 | defined(_X86_) || defined(__X86__) || defined(_M_IX86) || \ 58 | defined(__I86__) || defined(__IA32__) || defined(__THW_INTEL) 59 | # define YY_ARCH_X86 1 60 | # define YY_ARCH_32 1 61 | #elif defined(__arm64) || defined(__arm64__) || \ 62 | defined(__aarch64__) || defined(_M_ARM64) 63 | # define YY_ARCH_ARM64 1 64 | # define YY_ARCH_64 1 65 | #elif defined(__arm) || defined(__arm__) || defined(_ARM_) || \ 66 | defined(_ARM) || defined(_M_ARM) || defined(__TARGET_ARCH_ARM) 67 | # define YY_ARCH_ARM32 1 68 | # define YY_ARCH_32 1 69 | #endif 70 | 71 | #if !YY_ARCH_64 && YY_ARCH_32 72 | # if defined(_LP64) || defined(__LP64__) || defined(__64BIT__) 73 | # define YY_ARCH_64 1 74 | # endif 75 | #endif 76 | 77 | /* compiler builtin check (clang) */ 78 | #ifndef yy_has_builtin 79 | # ifdef __has_builtin 80 | # define yy_has_builtin(x) __has_builtin(x) 81 | # else 82 | # define yy_has_builtin(x) 0 83 | # endif 84 | #endif 85 | 86 | /* compiler attribute check (gcc/clang) */ 87 | #ifndef yy_has_attribute 88 | # ifdef __has_attribute 89 | # define yy_has_attribute(x) __has_attribute(x) 90 | # else 91 | # define yy_has_attribute(x) 0 92 | # endif 93 | #endif 94 | 95 | /* include check (gcc/clang) */ 96 | #ifndef yy_has_include 97 | # ifdef __has_include 98 | # define yy_has_include(x) __has_include(x) 99 | # else 100 | # define yy_has_include(x) 0 101 | # endif 102 | #endif 103 | 104 | /* inline */ 105 | #ifndef yy_inline 106 | # if _MSC_VER >= 1200 107 | # define yy_inline __forceinline 108 | # elif defined(_MSC_VER) 109 | # define yy_inline __inline 110 | # elif yy_has_attribute(always_inline) || __GNUC__ >= 4 111 | # define yy_inline __inline__ __attribute__((always_inline)) 112 | # elif defined(__clang__) || defined(__GNUC__) 113 | # define yy_inline __inline__ 114 | # elif defined(__cplusplus) || (__STDC__ >= 1 && __STDC_VERSION__ >= 199901L) 115 | # define yy_inline inline 116 | # else 117 | # define yy_inline 118 | # endif 119 | #endif 120 | 121 | /* noinline */ 122 | #ifndef yy_noinline 123 | # if _MSC_VER >= 1200 124 | # define yy_noinline __declspec(noinline) 125 | # elif yy_has_attribute(noinline) || __GNUC__ >= 4 126 | # define yy_noinline __attribute__((noinline)) 127 | # else 128 | # define yy_noinline 129 | # endif 130 | #endif 131 | 132 | /* likely */ 133 | #ifndef yy_likely 134 | # if yy_has_builtin(__builtin_expect) || __GNUC__ >= 4 135 | # define yy_likely(expr) __builtin_expect(!!(expr), 1) 136 | # else 137 | # define yy_likely(expr) (expr) 138 | # endif 139 | #endif 140 | 141 | /* unlikely */ 142 | #ifndef yy_unlikely 143 | # if yy_has_builtin(__builtin_expect) || __GNUC__ >= 4 144 | # define yy_unlikely(expr) __builtin_expect(!!(expr), 0) 145 | # else 146 | # define yy_unlikely(expr) (expr) 147 | # endif 148 | #endif 149 | 150 | /* stdint */ 151 | #if YY_HAS_STDINT_H || yy_has_include() || \ 152 | _MSC_VER >= 1600 || (__STDC__ >= 1 && __STDC_VERSION__ >= 199901L) || \ 153 | defined(_STDINT_H) || defined(_STDINT_H_) || defined(__CLANG_STDINT_H) || \ 154 | defined(_STDINT_H_INCLUDED) 155 | # include 156 | #elif defined(_MSC_VER) 157 | # if _MSC_VER < 1300 158 | typedef signed char int8_t; 159 | typedef signed short int16_t; 160 | typedef signed int int32_t; 161 | typedef unsigned char uint8_t; 162 | typedef unsigned short uint16_t; 163 | typedef unsigned int uint32_t; 164 | typedef signed __int64 int64_t; 165 | typedef unsigned __int64 uint64_t; 166 | # else 167 | typedef signed __int8 int8_t; 168 | typedef signed __int16 int16_t; 169 | typedef signed __int32 int32_t; 170 | typedef unsigned __int8 uint8_t; 171 | typedef unsigned __int16 uint16_t; 172 | typedef unsigned __int32 uint32_t; 173 | typedef signed __int64 int64_t; 174 | typedef unsigned __int64 uint64_t; 175 | # endif 176 | #endif 177 | 178 | /* stdbool */ 179 | #if YY_HAS_STDBOOL_H || yy_has_include() || \ 180 | _MSC_VER >= 1800 || (__STDC__ >= 1 && __STDC_VERSION__ >= 199901L) 181 | # include 182 | #elif !defined(__bool_true_false_are_defined) 183 | # define __bool_true_false_are_defined 1 184 | # if defined(__cplusplus) 185 | # if defined(__GNUC__) && !defined(__STRICT_ANSI__) 186 | # define _Bool bool 187 | # if __cplusplus < 201103L 188 | # define bool bool 189 | # define false false 190 | # define true true 191 | # endif 192 | # endif 193 | # else 194 | # define bool unsigned char 195 | # define true 1 196 | # define false 0 197 | # endif 198 | #endif 199 | 200 | /* assert */ 201 | #define yy_assert(expr) \ 202 | if (!(expr)) { \ 203 | fprintf(stderr, "Assertion failed: %s (%s: %d)\n", #expr, __FILE__, __LINE__); \ 204 | abort(); \ 205 | }; 206 | 207 | #define yy_assertf(expr, ...) \ 208 | if (!(expr)) { \ 209 | fprintf(stderr, "Assertion failed: %s (%s: %d)\n", #expr, __FILE__, __LINE__); \ 210 | fprintf(stderr, __VA_ARGS__); \ 211 | fprintf(stderr, "\n"); \ 212 | abort(); \ 213 | }; 214 | 215 | /* test */ 216 | #if yy_has_include("yy_xctest.h") 217 | # include "yy_xctest.h" 218 | # define yy_test_case(name) \ 219 | void name(void) 220 | #else 221 | # define yy_test_case(name) \ 222 | void name(void); \ 223 | int main(int argc, const char * argv[]) { \ 224 | name(); \ 225 | return 0; \ 226 | } \ 227 | void name(void) 228 | #endif 229 | 230 | /* Type define for primitive types. */ 231 | typedef float f32; 232 | typedef double f64; 233 | typedef int8_t i8; 234 | typedef uint8_t u8; 235 | typedef int16_t i16; 236 | typedef uint16_t u16; 237 | typedef int32_t i32; 238 | typedef uint32_t u32; 239 | typedef int64_t i64; 240 | typedef uint64_t u64; 241 | typedef size_t usize; 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /src/yybench_env.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench_env.h" 7 | 8 | #if defined(__ANDROID__) 9 | #include 10 | #endif 11 | 12 | 13 | const char *yy_env_get_os_desc(void) { 14 | #if defined(__MINGW64__) 15 | return "Windows (MinGW-w64)"; 16 | #elif defined(__MINGW32__) 17 | return "Windows (MinGW)"; 18 | #elif defined(__CYGWIN__) && defined(ARCH_64_DEFINED) 19 | return "Windows (Cygwin x64)"; 20 | #elif defined(__CYGWIN__) 21 | return "Windows (Cygwin x86)"; 22 | #elif defined(_WIN64) 23 | return "Windows 64-bit"; 24 | #elif defined(_WIN32) 25 | return "Windows 32-bit"; 26 | 27 | #elif defined(__APPLE__) 28 | static bool finished = false; 29 | static char os_desc[128] = {0}; 30 | static char os_ver[128] = {0}; 31 | size_t os_ver_size = sizeof(os_ver); 32 | if (finished) return os_desc; 33 | 34 | sysctlbyname("kern.osproductversion", (void *)os_ver, &os_ver_size, NULL, 0); 35 | if (strlen(os_ver) == 0) strcpy(os_ver, "unknown version"); 36 | 37 | # if TARGET_OS_IPHONE || TARGET_OS_IOS 38 | # if defined(YY_ARCH_64) 39 | sprintf(os_desc, "iOS %s (64-bit)", os_ver); 40 | # else 41 | sprintf(os_desc, "iOS %s (32-bit)", os_ver); 42 | # endif 43 | # elif TARGET_OS_OSX 44 | # if defined(YY_ARCH_64) 45 | sprintf(os_desc, "macOS %s (64-bit)", os_ver); 46 | # else 47 | sprintf(os_desc, "macOS %s (32-bit)", os_ver); 48 | # endif 49 | # elif TARGET_OS_WATCH 50 | # if defined(YY_ARCH_64) 51 | sprintf(os_desc, "watchOS %s (64-bit)", os_ver); 52 | # else 53 | sprintf(os_desc, "watchOS %s (32-bit)", os_ver); 54 | # endif 55 | # elif TARGET_OS_TV 56 | # if defined(YY_ARCH_64) 57 | sprintf(os_desc, "tvOS %s (64-bit)", os_ver); 58 | # else 59 | sprintf(os_desc, "tvOS %s (32-bit)", os_ver); 60 | # endif 61 | # else 62 | # if defined(YY_ARCH_64) 63 | sprintf(os_desc, "Unknown Apple OS %s (64-bit)", os_ver); 64 | # else 65 | sprintf(os_desc, "Unknown Apple OS %s (32-bit)", os_ver); 66 | # endif 67 | # endif 68 | finished = true; 69 | return os_desc; 70 | 71 | #elif defined(__ANDROID__) 72 | static bool finished = false; 73 | static char os_desc[128] = {0}; 74 | static char os_ver[128] = {0}; 75 | if (finished) return os_desc; 76 | __system_property_get("ro.build.version.release", os_ver); 77 | if (strlen(os_ver) == 0) strcpy(os_ver, "unknown version"); 78 | 79 | # if defined(YY_ARCH_64) 80 | sprintf(os_desc, "Android %s (64-bit)", os_ver); 81 | # else 82 | sprintf(os_desc, "Android %s (32-bit)", os_ver); 83 | # endif 84 | finished = true; 85 | return os_desc; 86 | 87 | #elif defined(__linux__) || defined(__linux) || defined(__gnu_linux__) 88 | # if defined(YY_ARCH_64) 89 | return "Linux 64-bit"; 90 | # else 91 | return "Linux 32-bit"; 92 | # endif 93 | 94 | #elif defined(__BSD__) || defined(__FreeBSD__) 95 | # if defined(YY_ARCH_64) 96 | return "BSD 64-bit"; 97 | # else 98 | return "BSD 32-bit"; 99 | # endif 100 | 101 | #else 102 | # if defined(YY_ARCH_64) 103 | return "Unknown OS 64-bit"; 104 | # else 105 | return "Unknown OS 32-bit"; 106 | # endif 107 | #endif 108 | } 109 | 110 | const char *yy_env_get_cpu_desc(void) { 111 | #if defined(__APPLE__) 112 | static char brand[256] = {0}; 113 | size_t size = sizeof(brand); 114 | static bool finished = false; 115 | struct utsname sysinfo; 116 | 117 | if (finished) return brand; 118 | /* works for macOS */ 119 | if (sysctlbyname("machdep.cpu.brand_string", (void *)brand, &size, NULL, 0) == 0) { 120 | if (strlen(brand) > 0) finished = true; 121 | } 122 | /* works for iOS, returns device model such as "iPhone9,1 ARM64_T8010" */ 123 | if (!finished) { 124 | uname(&sysinfo); 125 | const char *model = sysinfo.machine; 126 | const char *cpu = sysinfo.version; 127 | if (cpu) { 128 | cpu = strstr(cpu, "RELEASE_"); 129 | if (cpu) cpu += 8; 130 | } 131 | if (model || cpu) { 132 | snprintf(brand, sizeof(brand), "%s %s", model ? model : "", cpu ? cpu : ""); 133 | finished = true; 134 | } 135 | } 136 | if (!finished) { 137 | snprintf(brand, sizeof(brand), "Unknown CPU"); 138 | finished = true; 139 | } 140 | return brand; 141 | 142 | #elif defined(_WIN32) 143 | #if defined(__x86_64__) || defined(__amd64__) || \ 144 | defined(_M_IX86) || defined(_M_AMD64) 145 | static char brand[0x40] = { 0 }; 146 | static bool finished = false; 147 | int cpui[4] = { 0 }; 148 | int nexids, i; 149 | 150 | if (finished) return brand; 151 | __cpuid(cpui, 0x80000000); 152 | nexids = cpui[0]; 153 | if (nexids >= 0x80000004) { 154 | for (i = 2; i <= 4; i++) { 155 | memset(cpui, 0, sizeof(cpui)); 156 | __cpuidex(cpui, i + 0x80000000, 0); 157 | memcpy(brand + (i - 2) * sizeof(cpui), cpui, sizeof(cpui)); 158 | } 159 | finished = true; 160 | } 161 | if (!finished || strlen(brand) == 0) { 162 | snprintf(brand, sizeof(brand), "Unknown CPU"); 163 | finished = true; 164 | } 165 | return brand; 166 | # else 167 | return "Unknown CPU"; 168 | # endif 169 | 170 | #else 171 | # define BUF_LENGTH 1024 172 | static char buf[BUF_LENGTH], *res = NULL; 173 | static bool finished = false; 174 | const char *prefixes[] = { 175 | "model name", /* x86 */ 176 | "CPU part", /* arm */ 177 | "cpu model\t",/* mips */ 178 | "cpu\t" /* powerpc */ 179 | }; 180 | int i, len; 181 | FILE *fp; 182 | 183 | if (res) return res; 184 | fp = fopen("/proc/cpuinfo", "r"); 185 | if (fp) { 186 | while (!res) { 187 | memset(buf, 0, BUF_LENGTH); 188 | if (fgets(buf, BUF_LENGTH - 1, fp) == NULL) break; 189 | for (i = 0; i < (int)(sizeof(prefixes) / sizeof(char *)) && !res; i++) { 190 | if (strncmp(prefixes[i], buf, strlen(prefixes[i])) == 0) { 191 | res = buf + strlen(prefixes[i]); 192 | } 193 | } 194 | } 195 | fclose(fp); 196 | } 197 | if (res) { 198 | while (*res == ' ' || *res == '\t' || *res == ':') res++; 199 | for (i = 0, len = (int)strlen(res); i < len; i++) { 200 | if (res[i] == '\t') res[i] = ' '; 201 | if (res[i] == '\r' || res[i] == '\n') res[i] = '\0'; 202 | } 203 | } else { 204 | res = "Unknown CPU"; 205 | } 206 | finished = true; 207 | return res; 208 | #endif 209 | } 210 | 211 | const char *yy_env_get_compiler_desc(void) { 212 | static char buf[512] = {0}; 213 | static bool finished = false; 214 | if (finished) return buf; 215 | 216 | #if defined(__ICL) || defined(__ICC) || defined(__INTEL_COMPILER) 217 | int v, r; /* version, revision */ 218 | # if defined(__INTEL_COMPILER) 219 | v = __INTEL_COMPILER; 220 | # elif defined(__ICC) 221 | v = __ICC; 222 | # else 223 | v = __ICL; 224 | # endif 225 | r = (v - (v / 100) * 100) / 10; 226 | v = v / 100; 227 | snprintf(buf, sizeof(buf), "Intel C++ Compiler %d.%d", v, r); 228 | 229 | #elif defined(__ARMCC_VERSION) 230 | int v, r; 231 | v = __ARMCC_VERSION; /* PVVbbbb or Mmmuuxx */ 232 | r = (v - (v / 1000000) * 1000000) / 1000; 233 | v = v / 1000000; 234 | snprintf(buf, sizeof(buf), "ARM Compiler %d.%d", v, r); 235 | 236 | #elif defined(_MSC_VER) 237 | /* https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros */ 238 | const char *vc; 239 | # if _MSC_VER >= 1930 240 | vc = ""; 241 | # elif _MSC_VER >= 1920 242 | vc = " 2019"; 243 | # elif _MSC_VER >= 1910 244 | vc = " 2017"; 245 | # elif _MSC_VER >= 1900 246 | vc = " 2015"; 247 | # elif _MSC_VER >= 1800 248 | vc = " 2013"; 249 | # elif _MSC_VER >= 1700 250 | vc = " 2012"; 251 | # elif _MSC_VER >= 1600 252 | vc = " 2010"; 253 | # elif _MSC_VER >= 1500 254 | vc = " 2008"; 255 | # elif _MSC_VER >= 1400 256 | vc = " 2005"; 257 | # elif _MSC_VER >= 1310 258 | vc = " 7.1"; 259 | # elif _MSC_VER >= 1300 260 | vc = " 7.0"; 261 | # elif _MSC_VER >= 1200 262 | vc = " 6.0"; 263 | # else 264 | vc = ""; 265 | # endif 266 | snprintf(buf, sizeof(buf), "Microsoft Visual C++%s (%d)", vc, _MSC_VER); 267 | 268 | #elif defined(__clang__) 269 | # if defined(__apple_build_version__) 270 | /* Apple versions: https://en.wikipedia.org/wiki/Xcode#Latest_versions */ 271 | snprintf(buf, sizeof(buf), "Clang %d.%d.%d (Apple version)", 272 | __clang_major__, __clang_minor__, __clang_patchlevel__); 273 | # else 274 | snprintf(buf, sizeof(buf), "Clang %d.%d.%d", 275 | __clang_major__, __clang_minor__, __clang_patchlevel__); 276 | # endif 277 | 278 | #elif defined(__GNUC__) 279 | const char *ext; 280 | # if defined(__CYGWIN__) 281 | ext = " (Cygwin)"; 282 | # elif defined(__MINGW64__) 283 | ext = " (MinGW-w64)"; 284 | # elif defined(__MINGW32__) 285 | ext = " (MinGW)"; 286 | # else 287 | ext = ""; 288 | # endif 289 | # if defined(__GNUC_PATCHLEVEL__) 290 | snprintf(buf, sizeof(buf), "GCC %d.%d.%d%s", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__, ext); 291 | # else 292 | snprintf(buf, sizeof(buf), "GCC %d.%d%s", __GNUC__, __GNUC_MINOR__, ext); 293 | # endif 294 | 295 | #else 296 | snprintf(buf, sizeof(buf), "Unknown Compiler"); 297 | #endif 298 | 299 | finished = true; 300 | return buf; 301 | } 302 | -------------------------------------------------------------------------------- /src/yybench_env.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_env_h 7 | #define yybench_env_h 8 | 9 | #include "yybench_def.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | 16 | /*============================================================================== 17 | * Runtime Environment 18 | *============================================================================*/ 19 | 20 | /** Returns OS description. */ 21 | const char *yy_env_get_os_desc(void); 22 | 23 | /** Returns CPU description. */ 24 | const char *yy_env_get_cpu_desc(void); 25 | 26 | /** Returns compiler description. */ 27 | const char *yy_env_get_compiler_desc(void); 28 | 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/yybench_file.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench_file.h" 7 | 8 | 9 | /*============================================================================== 10 | * File Utils 11 | *============================================================================*/ 12 | 13 | bool yy_path_combine(char *buf, const char *path, ...) { 14 | if (!buf) return false; 15 | *buf = '\0'; 16 | if (!path) return false; 17 | 18 | usize len = strlen(path); 19 | memmove(buf, path, len); 20 | const char *hdr = buf; 21 | buf += len; 22 | 23 | va_list args; 24 | va_start(args, path); 25 | while (true) { 26 | const char *item = va_arg(args, const char *); 27 | if (!item) break; 28 | if (buf > hdr && *(buf - 1) != YY_DIR_SEPARATOR) { 29 | *buf++ = YY_DIR_SEPARATOR; 30 | } 31 | len = strlen(item); 32 | if (len && *item == YY_DIR_SEPARATOR) { 33 | len--; 34 | item++; 35 | } 36 | memmove(buf, item, len); 37 | buf += len; 38 | } 39 | va_end(args); 40 | 41 | *buf = '\0'; 42 | return true; 43 | } 44 | 45 | bool yy_path_remove_last(char *buf, const char *path) { 46 | usize len = path ? strlen(path) : 0; 47 | if (!buf) return false; 48 | *buf = '\0'; 49 | if (len == 0) return false; 50 | 51 | const char *cur = path + len - 1; 52 | if (*cur == YY_DIR_SEPARATOR) cur--; 53 | for (; cur >= path; cur--) { 54 | if (*cur == YY_DIR_SEPARATOR) break; 55 | } 56 | len = cur + 1 - path; 57 | memmove(buf, path, len); 58 | buf[len] = '\0'; 59 | return len > 0; 60 | } 61 | 62 | bool yy_path_get_last(char *buf, const char *path) { 63 | usize len = path ? strlen(path) : 0; 64 | const char *end, *cur; 65 | if (!buf) return false; 66 | *buf = '\0'; 67 | if (len == 0) return false; 68 | 69 | end = path + len - 1; 70 | if (*end == YY_DIR_SEPARATOR) end--; 71 | for (cur = end; cur >= path; cur--) { 72 | if (*cur == YY_DIR_SEPARATOR) break; 73 | } 74 | len = end - cur; 75 | memmove(buf, cur + 1, len); 76 | buf[len] = '\0'; 77 | return len > 0; 78 | } 79 | 80 | bool yy_path_append_ext(char *buf, const char *path, const char *ext) { 81 | usize len = path ? strlen(path) : 0; 82 | char tmp[YY_MAX_PATH]; 83 | char *cur = tmp; 84 | if (!buf) return false; 85 | 86 | memcpy(cur, path, len); 87 | cur += len; 88 | *cur++ = '.'; 89 | 90 | len = ext ? strlen(ext) : 0; 91 | memcpy(cur, ext, len); 92 | cur += len; 93 | *cur++ = '\0'; 94 | 95 | memcpy(buf, tmp, cur - tmp); 96 | return true; 97 | } 98 | 99 | bool yy_path_remove_ext(char *buf, const char *path) { 100 | if (!buf || !path) return false; 101 | usize len = strlen(path); 102 | memmove(buf, path, len + 1); 103 | for (char *cur = buf + len; cur >= buf; cur--) { 104 | if (*cur == YY_DIR_SEPARATOR) break; 105 | if (*cur == '.') { 106 | *cur = '\0'; 107 | return true; 108 | } 109 | } 110 | return false; 111 | } 112 | 113 | bool yy_path_get_ext(char *buf, const char *path) { 114 | if (!buf || !path) return false; 115 | usize len = strlen(path); 116 | for (const char *cur = path + len; cur >= path; cur--) { 117 | if (*cur == YY_DIR_SEPARATOR) break; 118 | if (*cur == '.') { 119 | memmove(buf, cur + 1, len - (cur - path)); 120 | return true; 121 | } 122 | } 123 | *buf = '\0'; 124 | return false; 125 | } 126 | 127 | 128 | 129 | bool yy_path_exist(const char *path) { 130 | if (!path || !strlen(path)) return false; 131 | #ifdef _WIN32 132 | DWORD attrs = GetFileAttributesA(path); 133 | return attrs != INVALID_FILE_ATTRIBUTES; 134 | #else 135 | struct stat attr; 136 | if (stat(path, &attr) != 0) return false; 137 | return true; 138 | #endif 139 | } 140 | 141 | bool yy_path_is_dir(const char *path) { 142 | if (!path || !strlen(path)) return false; 143 | #ifdef _WIN32 144 | DWORD attrs = GetFileAttributesA(path); 145 | return (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0; 146 | #else 147 | struct stat attr; 148 | if (stat(path, &attr) != 0) return false; 149 | return S_ISDIR(attr.st_mode); 150 | #endif 151 | } 152 | 153 | 154 | 155 | static int strcmp_func(void const *a, void const *b) { 156 | char const *astr = *(char const **)a; 157 | char const *bstr = *(char const **)b; 158 | return strcmp(astr, bstr); 159 | } 160 | 161 | char **yy_dir_read_opts(const char *path, int *count, bool full) { 162 | #ifdef _WIN32 163 | struct _finddata_t entry; 164 | intptr_t handle; 165 | int idx = 0, alc = 0; 166 | char **names = NULL, **names_tmp, *search; 167 | usize path_len = path ? strlen(path) : 0; 168 | 169 | if (count) *count = 0; 170 | if (path_len == 0) return NULL; 171 | search = malloc(path_len + 3); 172 | if (!search) return NULL; 173 | memcpy(search, path, path_len); 174 | if (search[path_len - 1] == '\\') path_len--; 175 | memcpy(search + path_len, "\\*\0", 3); 176 | 177 | handle = _findfirst(search, &entry); 178 | if (handle == -1) goto fail; 179 | 180 | alc = 4; 181 | names = malloc(alc * sizeof(char*)); 182 | if (!names) goto fail; 183 | 184 | do { 185 | char *name = (char *)entry.name; 186 | if (!name || !strlen(name)) continue; 187 | if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; 188 | name = _strdup(name); 189 | if (!name) goto fail; 190 | if (idx + 1 >= alc) { 191 | alc *= 2; 192 | names_tmp = realloc(names, alc * sizeof(char*)); 193 | if (!names_tmp) goto fail; 194 | names = names_tmp; 195 | } 196 | if (full) { 197 | char *fullpath = malloc(strlen(path) + strlen(name) + 4); 198 | if (!fullpath) goto fail; 199 | yy_path_combine(fullpath, path, name, NULL); 200 | free(name); 201 | if (fullpath) name = fullpath; 202 | else break; 203 | } 204 | names[idx] = name; 205 | idx++; 206 | } while (_findnext(handle, &entry) == 0); 207 | _findclose(handle); 208 | 209 | if (idx > 1) qsort(names, idx, sizeof(char *), strcmp_func); 210 | names[idx] = NULL; 211 | if (count) *count = idx; 212 | return names; 213 | 214 | fail: 215 | if (handle != -1)_findclose(handle); 216 | if (search) free(search); 217 | if (names) free(names); 218 | return NULL; 219 | 220 | #else 221 | DIR *dir = NULL; 222 | struct dirent *entry; 223 | int idx = 0, alc = 0; 224 | char **names = NULL, **names_tmp; 225 | 226 | if (count) *count = 0; 227 | if (!path || !strlen(path) || !(dir = opendir(path))) { 228 | goto fail; 229 | } 230 | 231 | alc = 4; 232 | names = calloc(1, alc * sizeof(char *)); 233 | if (!names) goto fail; 234 | 235 | while ((entry = readdir(dir))) { 236 | char *name = (char *)entry->d_name; 237 | if (!name || !strlen(name)) continue; 238 | if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; 239 | if (idx + 1 >= alc) { 240 | alc *= 2; 241 | names_tmp = realloc(names, alc * sizeof(char *)); 242 | if (!names_tmp) 243 | goto fail; 244 | names = names_tmp; 245 | } 246 | name = strdup(name); 247 | if (!name) goto fail; 248 | if (full) { 249 | char *fullpath = malloc(strlen(path) + strlen(name) + 4); 250 | if (!fullpath) { 251 | free(name); 252 | goto fail; 253 | } 254 | yy_path_combine(fullpath, path, name, NULL); 255 | free(name); 256 | if (fullpath) name = fullpath; 257 | else break; 258 | } 259 | names[idx] = name; 260 | idx++; 261 | } 262 | closedir(dir); 263 | if (idx > 1) qsort(names, idx, sizeof(char *), strcmp_func); 264 | names[idx] = NULL; 265 | if (count) *count = idx; 266 | return names; 267 | 268 | fail: 269 | if (dir) closedir(dir); 270 | yy_dir_free(names); 271 | return NULL; 272 | #endif 273 | } 274 | 275 | char **yy_dir_read(const char *path, int *count) { 276 | return yy_dir_read_opts(path, count, false); 277 | } 278 | 279 | char **yy_dir_read_full(const char *path, int *count) { 280 | return yy_dir_read_opts(path, count, true); 281 | } 282 | 283 | void yy_dir_free(char **names) { 284 | if (names) { 285 | for (int i = 0; ; i++) { 286 | if (names[i]) free(names[i]); 287 | else break; 288 | } 289 | free(names); 290 | } 291 | } 292 | 293 | 294 | 295 | bool yy_file_read(const char *path, u8 **dat, usize *len) { 296 | return yy_file_read_with_padding(path, dat, len, 1); // for string 297 | } 298 | 299 | bool yy_file_read_with_padding(const char *path, u8 **dat, usize *len, usize padding) { 300 | if (!path || !strlen(path)) return false; 301 | if (!dat || !len) return false; 302 | 303 | FILE *file = NULL; 304 | #if _MSC_VER >= 1400 305 | if (fopen_s(&file, path, "rb") != 0) return false; 306 | #else 307 | file = fopen(path, "rb"); 308 | #endif 309 | if (file == NULL) return false; 310 | if (fseek(file, 0, SEEK_END) != 0) { 311 | fclose(file); 312 | return false; 313 | } 314 | long file_size = ftell(file); 315 | if (file_size < 0) { 316 | fclose(file); 317 | return false; 318 | } 319 | if (fseek(file, 0, SEEK_SET) != 0) { 320 | fclose(file); 321 | return false; 322 | } 323 | void *buf = malloc((usize)file_size + padding); 324 | if (buf == NULL) { 325 | fclose(file); 326 | return false; 327 | } 328 | 329 | if (file_size > 0) { 330 | #if _MSC_VER >= 1400 331 | if (fread_s(buf, file_size, file_size, 1, file) != 1) { 332 | free(buf); 333 | fclose(file); 334 | return false; 335 | } 336 | #else 337 | if (fread(buf, file_size, 1, file) != 1) { 338 | free(buf); 339 | fclose(file); 340 | return false; 341 | } 342 | #endif 343 | } 344 | fclose(file); 345 | 346 | memset((char *)buf + file_size, 0, padding); 347 | *dat = (u8 *)buf; 348 | *len = (usize)file_size; 349 | return true; 350 | } 351 | 352 | bool yy_file_write(const char *path, u8 *dat, usize len) { 353 | if (!path || !strlen(path)) return false; 354 | if (len && !dat) return false; 355 | 356 | FILE *file = NULL; 357 | #if _MSC_VER >= 1400 358 | if (fopen_s(&file, path, "wb") != 0) return false; 359 | #else 360 | file = fopen(path, "wb"); 361 | #endif 362 | if (file == NULL) return false; 363 | if (fwrite(dat, len, 1, file) != 1) { 364 | fclose(file); 365 | return false; 366 | } 367 | if (fclose(file) != 0) { 368 | file = NULL; 369 | return false; 370 | } 371 | return true; 372 | } 373 | 374 | bool yy_file_delete(const char *path) { 375 | if (!path || !*path) return false; 376 | return remove(path) == 0; 377 | } 378 | 379 | 380 | 381 | /*============================================================================== 382 | * Data Reader 383 | *============================================================================*/ 384 | 385 | bool yy_dat_init_with_file(yy_dat *dat, const char *path) { 386 | u8 *mem; 387 | usize len; 388 | if (!dat) return false; 389 | memset(dat, 0, sizeof(yy_dat)); 390 | if (!yy_file_read(path, &mem, &len)) return false; 391 | dat->hdr = mem; 392 | dat->cur = mem; 393 | dat->end = mem + len; 394 | dat->need_free = true; 395 | return true; 396 | } 397 | 398 | bool yy_dat_init_with_mem(yy_dat *dat, u8 *mem, usize len) { 399 | if (!dat) return false; 400 | if (len && !mem) return false; 401 | dat->hdr = mem; 402 | dat->cur = mem; 403 | dat->end = mem + len; 404 | dat->need_free = false; 405 | return true; 406 | } 407 | 408 | void yy_dat_release(yy_dat *dat) { 409 | yy_buf_release(dat); 410 | } 411 | 412 | void yy_dat_reset(yy_dat *dat) { 413 | if (dat) dat->cur = dat->hdr; 414 | } 415 | char *yy_dat_read_line(yy_dat *dat, usize *len) { 416 | if (len) *len = 0; 417 | if (!dat || dat->cur >= dat->end) return NULL; 418 | 419 | u8 *str = dat->cur; 420 | u8 *cur = dat->cur; 421 | u8 *end = dat->end; 422 | while (cur < end && *cur != '\r' && *cur != '\n' && *cur != '\0') cur++; 423 | if (len) *len = cur - str; 424 | if (cur < end) { 425 | if (cur + 1 < end && *cur == '\r' && cur[1] == '\n') cur += 2; 426 | else if (*cur == '\r' || *cur == '\n' || *cur == '\0') cur++; 427 | } 428 | dat->cur = cur; 429 | return (char *)str; 430 | } 431 | 432 | char *yy_dat_copy_line(yy_dat *dat, usize *len) { 433 | if (len) *len = 0; 434 | usize _len; 435 | char *_str = yy_dat_read_line(dat, &_len); 436 | if (!_str) return NULL; 437 | char *str = malloc(_len + 1); 438 | if (!str) return NULL; 439 | memcpy(str, _str, _len); 440 | str[_len] = '\0'; 441 | if (len) *len = _len; 442 | return str; 443 | } 444 | 445 | -------------------------------------------------------------------------------- /src/yybench_file.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_file_h 7 | #define yybench_file_h 8 | 9 | #include "yybench_def.h" 10 | #include "yybench_str.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | 17 | /*============================================================================== 18 | * File Utils 19 | *============================================================================*/ 20 | 21 | #ifdef _WIN32 22 | #define YY_DIR_SEPARATOR '\\' 23 | #else 24 | #define YY_DIR_SEPARATOR '/' 25 | #endif 26 | #define YY_MAX_PATH 4096 27 | 28 | /** Combine multiple component into a path, store the result to the buffer. 29 | The input parameter list should end with NULL. 30 | Return false if input is NULL. 31 | For example: yy_path_combine(buf, "usr", "src", "test.c", NULL); */ 32 | #if yy_has_attribute(sentinel) 33 | __attribute__((sentinel)) 34 | #endif 35 | bool yy_path_combine(char *buf, const char *path, ...); 36 | 37 | /** Remove the last component of path, copy the result to the buffer. 38 | Return false if input is NULL or no last component. */ 39 | bool yy_path_remove_last(char *buf, const char *path); 40 | 41 | /** Get the last component of path, copy it to the buffer. 42 | Return false if input is NULL or no last component. */ 43 | bool yy_path_get_last(char *buf, const char *path); 44 | 45 | /** Append a file extension to the path, copy the result to the buffer. 46 | Return false if input is NULL. */ 47 | bool yy_path_append_ext(char *buf, const char *path, const char *ext); 48 | 49 | /** Remove the file extension, copy the result to the buffer. 50 | Return false if input is NULL or no extension. */ 51 | bool yy_path_remove_ext(char *buf, const char *path); 52 | 53 | /** Get the file extension, copy it to the buffer. 54 | Return false if input is NULL or no extension. */ 55 | bool yy_path_get_ext(char *buf, const char *path); 56 | 57 | 58 | /** Returns whether a path exist. */ 59 | bool yy_path_exist(const char *path); 60 | 61 | /** Returns whether a path is directory. */ 62 | bool yy_path_is_dir(const char *path); 63 | 64 | 65 | /** Returns content file names of a dir. Returns NULL on error. 66 | The result should be released by yy_dir_free() */ 67 | char **yy_dir_read(const char *path, int *count); 68 | 69 | /** Returns content file full paths of a dir. Returns NULL on error. 70 | The result should be released by yy_dir_free() */ 71 | char **yy_dir_read_full(const char *path, int *count); 72 | 73 | /** Free the return value of yy_dir_read(). */ 74 | void yy_dir_free(char **names); 75 | 76 | 77 | /** Read a file to memory, dat should be release with free(). */ 78 | bool yy_file_read(const char *path, u8 **dat, usize *len); 79 | 80 | /** Read a file to memory with zero padding, dat should be release with free(). */ 81 | bool yy_file_read_with_padding(const char *path, u8 **dat, usize *len, usize padding); 82 | 83 | /** Write data to file, overwrite if exist. */ 84 | bool yy_file_write(const char *path, u8 *dat, usize len); 85 | 86 | /** Delete a file, returns true if success. */ 87 | bool yy_file_delete(const char *path); 88 | 89 | 90 | /*============================================================================== 91 | * Data Reader 92 | *============================================================================*/ 93 | 94 | /** A data reader */ 95 | typedef struct yy_buf yy_dat; 96 | 97 | /** Initialize a data reader with file. */ 98 | bool yy_dat_init_with_file(yy_dat *dat, const char *path); 99 | 100 | /** Initialize a data reader with memory (no copy). */ 101 | bool yy_dat_init_with_mem(yy_dat *dat, u8 *mem, usize len); 102 | 103 | /** Release the data reader. */ 104 | void yy_dat_release(yy_dat *dat); 105 | 106 | /** Reset the cursor of data reader. */ 107 | void yy_dat_reset(yy_dat *dat); 108 | 109 | /** Read a line from data reader (NULL on end or error). 110 | The cursor will moved to next line. 111 | The string is not null-terminated. */ 112 | char *yy_dat_read_line(yy_dat *dat, usize *len); 113 | 114 | /** Read and copy a line from data reader (NULL on end or error). 115 | The cursor will moved to next line. 116 | The return value should be release with free(). */ 117 | char *yy_dat_copy_line(yy_dat *dat, usize *len); 118 | 119 | 120 | #ifdef __cplusplus 121 | } 122 | #endif 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /src/yybench_perf.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench_perf.h" 7 | #include 8 | 9 | 10 | /*============================================================================== 11 | * Linux 12 | *============================================================================*/ 13 | 14 | #if YY_LINUX_HAS_PERF 15 | 16 | struct yy_perf { 17 | u32 count; 18 | u32 capacity; 19 | u64 *events; 20 | const char **names; 21 | u64 *buffer; 22 | u64 *ids; 23 | u64 *counters; 24 | int fd; 25 | bool is_opened; 26 | bool is_counting; 27 | }; 28 | 29 | static const char *perf_event_get_name(u64 event) { 30 | // see 31 | u32 type = PERF_EVENT_GET_TYPE(event); 32 | u32 cfg = PERF_EVENT_GET_CONFIG(event); 33 | switch (type) { 34 | case PERF_TYPE_HARDWARE: 35 | switch (cfg) { 36 | case 0: return "cpu-cycles"; 37 | case 1: return "instructions"; 38 | case 2: return "cache-references"; 39 | case 3: return "cache-misses"; 40 | case 4: return "branches"; 41 | case 5: return "branch-misses"; 42 | case 6: return "bus-cycles"; 43 | case 7: return "stalled-cycles-frontend"; 44 | case 8: return "stalled-cycles-backend"; 45 | case 9: return "ref-cpu-cycles"; 46 | default: return "unknown-hardware-event"; 47 | } 48 | case PERF_TYPE_SOFTWARE: 49 | switch (cfg) { 50 | case 0: return "cpu-clock"; 51 | case 1: return "task-clock"; 52 | case 2: return "page-faults"; 53 | case 3: return "context-switches"; 54 | case 4: return "cpu-migrations"; 55 | case 5: return "page-faults-min"; 56 | case 6: return "page-faults-maj"; 57 | case 7: return "alignment-faults"; 58 | case 8: return "emulation-faults"; 59 | case 9: return "dummy"; 60 | case 10: return "bpf-output"; 61 | default: return "unknown-software-event"; 62 | } 63 | case PERF_TYPE_HW_CACHE: 64 | switch (cfg) { 65 | case (0 | (0 << 8) | (0 << 16)): return "L1d-read"; 66 | case (0 | (0 << 8) | (1 << 16)): return "L1d-read-misses"; 67 | case (0 | (1 << 8) | (0 << 16)): return "L1d-write"; 68 | case (0 | (1 << 8) | (1 << 16)): return "L1d-write-misses"; 69 | case (0 | (2 << 8) | (0 << 16)): return "L1d-prefetch"; 70 | case (0 | (2 << 8) | (1 << 16)): return "L1d-prefetch-misses"; 71 | 72 | case (1 | (0 << 8) | (0 << 16)): return "L1i-read"; 73 | case (1 | (0 << 8) | (1 << 16)): return "L1i-read-misses"; 74 | case (1 | (1 << 8) | (0 << 16)): return "L1i-write"; 75 | case (1 | (1 << 8) | (1 << 16)): return "L1i-write-misses"; 76 | case (1 | (2 << 8) | (0 << 16)): return "L1i-prefetch"; 77 | case (1 | (2 << 8) | (1 << 16)): return "L1i-prefetch-misses"; 78 | 79 | case (2 | (0 << 8) | (0 << 16)): return "LLC-read"; 80 | case (2 | (0 << 8) | (1 << 16)): return "LLC-read-misses"; 81 | case (2 | (1 << 8) | (0 << 16)): return "LLC-write"; 82 | case (2 | (1 << 8) | (1 << 16)): return "LLC-write-misses"; 83 | case (2 | (2 << 8) | (0 << 16)): return "LLC-prefetch"; 84 | case (2 | (2 << 8) | (1 << 16)): return "LLC-prefetch-misses"; 85 | 86 | case (3 | (0 << 8) | (0 << 16)): return "TDLB-read"; 87 | case (3 | (0 << 8) | (1 << 16)): return "TDLB-read-misses"; 88 | case (3 | (1 << 8) | (0 << 16)): return "TDLB-write"; 89 | case (3 | (1 << 8) | (1 << 16)): return "TDLB-write-misses"; 90 | case (3 | (2 << 8) | (0 << 16)): return "TDLB-prefetch"; 91 | case (3 | (2 << 8) | (1 << 16)): return "TDLB-prefetch-misses"; 92 | 93 | case (4 | (0 << 8) | (0 << 16)): return "ITLB-read"; 94 | case (4 | (0 << 8) | (1 << 16)): return "ITLB-read-misses"; 95 | case (4 | (1 << 8) | (0 << 16)): return "ITLB-write"; 96 | case (4 | (1 << 8) | (1 << 16)): return "ITLB-write-misses"; 97 | case (4 | (2 << 8) | (0 << 16)): return "ITLB-prefetch"; 98 | case (4 | (2 << 8) | (1 << 16)): return "ITLB-prefetch-misses"; 99 | 100 | case (5 | (0 << 8) | (0 << 16)): return "BPU-read"; 101 | case (5 | (0 << 8) | (1 << 16)): return "BPU-read-misses"; 102 | case (5 | (1 << 8) | (0 << 16)): return "BPU-write"; 103 | case (5 | (1 << 8) | (1 << 16)): return "BPU-write-misses"; 104 | case (5 | (2 << 8) | (0 << 16)): return "BPU-prefetch"; 105 | case (5 | (2 << 8) | (1 << 16)): return "BPU-prefetch-misses"; 106 | 107 | case (6 | (0 << 8) | (0 << 16)): return "node-read"; 108 | case (6 | (0 << 8) | (1 << 16)): return "node-read-misses"; 109 | case (6 | (1 << 8) | (0 << 16)): return "node-write"; 110 | case (6 | (1 << 8) | (1 << 16)): return "node-write-misses"; 111 | case (6 | (2 << 8) | (0 << 16)): return "node-prefetch"; 112 | case (6 | (2 << 8) | (1 << 16)): return "node-prefetch-misses"; 113 | default: return "unknown-cache-event"; 114 | } 115 | case PERF_TYPE_RAW: 116 | return "raw"; 117 | default: 118 | return "unknown"; 119 | } 120 | } 121 | 122 | static u64 perf_event_conv(yy_perf_event ev) { 123 | switch (ev) { 124 | case YY_PERF_EVENT_CYCLES: 125 | return PERF_EVENT_MAKE(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); 126 | case YY_PERF_EVENT_INSTRUCTIONS: 127 | return PERF_EVENT_MAKE(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); 128 | case YY_PERF_EVENT_BRANCHES: 129 | return PERF_EVENT_MAKE(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); 130 | case YY_PERF_EVENT_BRANCH_MISSES: 131 | return PERF_EVENT_MAKE(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); 132 | case YY_PERF_EVENT_L1I_LOADS: 133 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_L1I, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS); 134 | case YY_PERF_EVENT_L1I_LOAD_MISSES: 135 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_L1I, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS); 136 | case YY_PERF_EVENT_L1D_LOADS: 137 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_L1D, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS); 138 | case YY_PERF_EVENT_L1D_LOAD_MISSES: 139 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_L1D, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS); 140 | case YY_PERF_EVENT_L1D_STORES: 141 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_L1D, PERF_COUNT_HW_CACHE_OP_WRITE, PERF_COUNT_HW_CACHE_RESULT_ACCESS); 142 | case YY_PERF_EVENT_L1D_STORE_MISSES: 143 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_L1D, PERF_COUNT_HW_CACHE_OP_WRITE, PERF_COUNT_HW_CACHE_RESULT_MISS); 144 | case YY_PERF_EVENT_LLC_LOADS: 145 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_LL, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS); 146 | case YY_PERF_EVENT_LLC_LOAD_MISSES: 147 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_LL, PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS); 148 | case YY_PERF_EVENT_LLC_STORES: 149 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_LL, PERF_COUNT_HW_CACHE_OP_WRITE, PERF_COUNT_HW_CACHE_RESULT_ACCESS); 150 | case YY_PERF_EVENT_LLC_STORE_MISSES: 151 | return PERF_EVENT_MAKE_CACHE(PERF_COUNT_HW_CACHE_LL, PERF_COUNT_HW_CACHE_OP_WRITE, PERF_COUNT_HW_CACHE_RESULT_MISS); 152 | default: return 0; 153 | } 154 | } 155 | 156 | static bool perf_capacity_grow(yy_perf *perf, u32 capacity) { 157 | u64 *events = NULL; 158 | const char **names = NULL; 159 | u64 *buffer = NULL; 160 | u64 *ids = NULL; 161 | u64 *counters = NULL; 162 | 163 | events = calloc(capacity, sizeof(u64)); 164 | if (!events) goto fail; 165 | names = calloc(capacity, sizeof(char *)); 166 | if (!names) goto fail; 167 | buffer = calloc((capacity + 1) * 2, sizeof(u64)); 168 | if (!buffer) goto fail; 169 | ids = calloc(capacity, sizeof(u64)); 170 | if (!ids) goto fail; 171 | counters = calloc(capacity, sizeof(u64)); 172 | if (!counters) goto fail; 173 | if (perf->count) { 174 | memcpy(events, perf->events, perf->count * sizeof(u64)); 175 | memcpy(names, perf->names, perf->count * sizeof(char *)); 176 | memcpy(buffer, perf->buffer, (perf->count + 1) * 2 * sizeof(u64)); 177 | memcpy(counters, perf->counters, perf->count * sizeof(u64)); 178 | free(perf->events); 179 | free(perf->names); 180 | free(perf->buffer); 181 | free(perf->ids); 182 | free(perf->counters); 183 | } 184 | perf->events = events; 185 | perf->names = names; 186 | perf->buffer = buffer; 187 | perf->ids = ids; 188 | perf->counters = counters; 189 | perf->capacity = capacity; 190 | return true; 191 | 192 | fail: 193 | if (events) free(events); 194 | if (names) free(names); 195 | if (buffer) free(buffer); 196 | if (ids) free(ids); 197 | if (counters) free(counters); 198 | } 199 | 200 | bool perf_open_test(u64 ev) { 201 | struct perf_event_attr pe = {0}; 202 | pe.type = PERF_EVENT_GET_TYPE(ev); 203 | pe.config = PERF_EVENT_GET_CONFIG(ev); 204 | pe.size = sizeof(struct perf_event_attr); 205 | pe.disabled = 1; 206 | int fd = yy_perf_event_open(&pe, 0, -1, -1, 0); 207 | if (fd == -1) { 208 | return false; 209 | } 210 | close(fd); 211 | return true; 212 | } 213 | 214 | bool yy_perf_load(bool print_message_on_error) { 215 | static bool loaded = false; 216 | if (loaded) return true; 217 | 218 | if (!perf_open_test(PERF_EVENT_MAKE(PERF_TYPE_HARDWARE, YY_PERF_EVENT_CYCLES))) { 219 | if (print_message_on_error) { 220 | fprintf(stderr, "Cannot open perf event, this requires root privileges.\n"); 221 | fprintf(stderr, "If it runs on a VM, virtual CPU performance counters should be enabled.\n"); 222 | } 223 | return false; 224 | } 225 | 226 | loaded = true; 227 | return true; 228 | } 229 | 230 | yy_perf *yy_perf_new(void) { 231 | yy_perf *perf = (yy_perf *)calloc(1, sizeof(yy_perf)); 232 | if (!perf) return NULL; 233 | if (!perf_capacity_grow(perf, 8)) return false; 234 | return perf; 235 | } 236 | 237 | void yy_perf_free(yy_perf *perf) { 238 | if (!perf) return; 239 | yy_perf_close(perf); 240 | if (perf->events) free(perf->events); 241 | if (perf->names) free(perf->names); 242 | if (perf->buffer) free(perf->buffer); 243 | if (perf->ids) free(perf->ids); 244 | if (perf->counters) free(perf->counters); 245 | memset(perf, 0, sizeof(yy_perf)); 246 | free(perf); 247 | } 248 | 249 | bool yy_perf_add_event(yy_perf *perf, yy_perf_event event) { 250 | u64 ev = perf_event_conv(event); 251 | return yy_perf_add_event_linux(perf, ev, NULL); 252 | } 253 | 254 | bool yy_perf_remove_all_events(yy_perf *perf) { 255 | if (!perf) return false; 256 | if (perf->is_opened || perf->is_counting) return false; 257 | perf->count = 0; 258 | return true; 259 | } 260 | 261 | bool yy_perf_event_available(yy_perf *perf, yy_perf_event event) { 262 | u64 ev = perf_event_conv(event); 263 | return yy_perf_event_available_linux(perf, ev); 264 | } 265 | 266 | bool yy_perf_add_event_apple(yy_perf *perf, const char *ev_name, const char *ev_alias) { 267 | return false; 268 | } 269 | 270 | bool yy_perf_event_available_apple(yy_perf *perf, const char *ev_name) { 271 | return false; 272 | } 273 | 274 | bool yy_perf_add_event_linux(yy_perf *perf, u64 ev_value, const char *ev_alias) { 275 | if (!perf) return false; 276 | if (perf->is_opened || perf->is_counting) return false; 277 | if (!yy_perf_event_available_linux(perf, ev_value)) return false; 278 | 279 | if (perf->count >= perf->capacity) { 280 | if (!perf_capacity_grow(perf, perf->capacity * 2)) { 281 | return false; 282 | } 283 | } 284 | 285 | perf->events[perf->count] = ev_value; 286 | perf->names[perf->count] = ev_alias ? ev_alias : perf_event_get_name(ev_value); 287 | perf->count++; 288 | return true; 289 | } 290 | 291 | bool yy_perf_event_available_linux(yy_perf *perf, u64 ev_value) { 292 | return perf_open_test(ev_value); 293 | } 294 | 295 | u32 yy_perf_get_event_count(yy_perf *perf) { 296 | if (perf) return perf->count; 297 | return 0; 298 | } 299 | 300 | const char **yy_perf_get_event_names(yy_perf *perf) { 301 | if (!perf || !perf->count) return NULL; 302 | return perf->names; 303 | } 304 | 305 | bool yy_perf_open(yy_perf *perf) { 306 | if (!perf) return false; 307 | if (perf->count == 0) return false; 308 | if (perf->is_opened) return true; 309 | 310 | struct perf_event_attr pe = {0}; 311 | int ret = 0; 312 | int fd = 0; 313 | pid_t pid = 0; // calling process 314 | int cpu = -1; // any CPU 315 | int group = -1; // no group 316 | unsigned long long flags = 0; // no flag 317 | 318 | pe.size = sizeof(struct perf_event_attr); 319 | pe.disabled = 1; 320 | pe.exclude_kernel = 1; 321 | pe.exclude_hv = 1; 322 | pe.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; 323 | 324 | perf->fd = -1; 325 | for (u32 i = 0; i < perf->count; i++) { 326 | u64 ev = perf->events[i]; 327 | pe.type = PERF_EVENT_GET_TYPE(ev); 328 | pe.config = PERF_EVENT_GET_CONFIG(ev); 329 | fd = yy_perf_event_open(&pe, pid, cpu, group, flags); 330 | if (fd == -1) { 331 | if (perf->fd != -1) close(perf->fd); 332 | return false; 333 | } 334 | if (perf->fd == -1) perf->fd = fd; 335 | if (group == -1) group = fd; // add to same group 336 | ret = ioctl(fd, PERF_EVENT_IOC_ID, &perf->ids[i]); 337 | if (ret == -1) { 338 | close(perf->fd); 339 | return false; 340 | } 341 | } 342 | memset(perf->counters, 0, perf->count * sizeof(u64)); 343 | perf->is_opened = true; 344 | return true; 345 | } 346 | 347 | bool yy_perf_close(yy_perf *perf) { 348 | if (!perf) return false; 349 | if (perf->is_counting) yy_perf_stop_counting(perf); 350 | if (perf->is_opened) { 351 | close(perf->fd); 352 | perf->is_opened = false; 353 | memset(perf->counters, 0, perf->count * sizeof(u64)); 354 | } 355 | return true; 356 | } 357 | 358 | bool yy_perf_is_opened(yy_perf *perf) { 359 | return perf ? perf->is_opened : false; 360 | } 361 | 362 | bool yy_perf_start_counting(yy_perf *perf) { 363 | if (!perf) return false; 364 | if (!perf->is_opened) return false; 365 | if (perf->is_counting) return true; 366 | 367 | if (ioctl(perf->fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { 368 | return false; 369 | } 370 | if (ioctl(perf->fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { 371 | return false; 372 | } 373 | perf->is_counting = true; 374 | return true; 375 | } 376 | 377 | bool yy_perf_stop_counting(yy_perf *perf) { 378 | if (!perf) return false; 379 | if (!perf->is_counting) return false; 380 | 381 | perf->is_counting = false; 382 | if (ioctl(perf->fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) return false; 383 | return true; 384 | } 385 | 386 | bool yy_perf_is_counting(yy_perf *perf) { 387 | return perf ? perf->is_counting : false; 388 | } 389 | 390 | u64 *yy_perf_get_counters(yy_perf *perf) { 391 | if (!perf) return false; 392 | if (!perf->is_opened) return false; 393 | 394 | // read counter data 395 | if (read(perf->fd, perf->buffer, (perf->count * 2 + 1) * sizeof(u64)) == -1) return false; 396 | 397 | // data layout: [count, val1, id1, val2, id2, ...] 398 | for (u32 i = 0; i < perf->count; i++) { 399 | perf->counters[i] = perf->buffer[i * 2 + 1]; 400 | } 401 | return perf->counters; 402 | } 403 | 404 | #endif 405 | 406 | 407 | 408 | 409 | /*============================================================================== 410 | * Apple 411 | *============================================================================*/ 412 | 413 | #if YY_APPLE_HAS_PERF 414 | 415 | #include 416 | 417 | bool kperf_load(bool print_message_on_error) { 418 | static bool loaded = false; 419 | if (loaded) return true; 420 | 421 | void *kperf = dlopen("/System/Library/PrivateFrameworks/kperf.framework/kperf", RTLD_LAZY); 422 | if (!kperf) { 423 | if (print_message_on_error) { 424 | fprintf(stderr, "fail to load: kperf.framework, message: %s\n", dlerror()); 425 | } 426 | return false; 427 | } 428 | void *kperfdata = dlopen("/System/Library/PrivateFrameworks/kperfdata.framework/kperfdata", RTLD_LAZY); 429 | if (!kperfdata) { 430 | if (print_message_on_error) { 431 | fprintf(stderr, "fail to load: kperfdata.framework, message: %s\n", dlerror()); 432 | } 433 | return false; 434 | } 435 | 436 | #define load_symbol(handle, symbol) \ 437 | *(void **)&symbol = dlsym(handle, #symbol); \ 438 | if (!symbol) { printf("fail to load: %s\n", #symbol); return false; } 439 | 440 | load_symbol(kperf, kpc_pmu_version); 441 | load_symbol(kperf, kpc_cpu_string); 442 | load_symbol(kperf, kpc_set_counting); 443 | load_symbol(kperf, kpc_get_counting); 444 | load_symbol(kperf, kpc_set_thread_counting); 445 | load_symbol(kperf, kpc_get_thread_counting); 446 | load_symbol(kperf, kpc_get_config_count); 447 | load_symbol(kperf, kpc_get_counter_count); 448 | load_symbol(kperf, kpc_set_config); 449 | load_symbol(kperf, kpc_get_config); 450 | load_symbol(kperf, kpc_get_cpu_counters); 451 | load_symbol(kperf, kpc_get_thread_counters); 452 | load_symbol(kperf, kpc_force_all_ctrs_set); 453 | load_symbol(kperf, kpc_force_all_ctrs_get); 454 | load_symbol(kperf, kperf_reset); 455 | 456 | load_symbol(kperfdata, kpep_config_create); 457 | load_symbol(kperfdata, kpep_config_free); 458 | load_symbol(kperfdata, kpep_config_add_event); 459 | load_symbol(kperfdata, kpep_config_remove_event); 460 | load_symbol(kperfdata, kpep_config_force_counters); 461 | load_symbol(kperfdata, kpep_config_events_count); 462 | load_symbol(kperfdata, kpep_config_events); 463 | load_symbol(kperfdata, kpep_config_kpc); 464 | load_symbol(kperfdata, kpep_config_kpc_count); 465 | load_symbol(kperfdata, kpep_config_kpc_classes); 466 | load_symbol(kperfdata, kpep_config_apply); 467 | load_symbol(kperfdata, kpep_db_create); 468 | load_symbol(kperfdata, kpep_db_free); 469 | load_symbol(kperfdata, kpep_db_name); 470 | load_symbol(kperfdata, kpep_db_architecture); 471 | load_symbol(kperfdata, kpep_db_aliases_count); 472 | load_symbol(kperfdata, kpep_db_aliases); 473 | load_symbol(kperfdata, kpep_db_counters_count); 474 | load_symbol(kperfdata, kpep_db_events_count); 475 | load_symbol(kperfdata, kpep_db_events); 476 | load_symbol(kperfdata, kpep_db_event); 477 | load_symbol(kperfdata, kpep_event_name); 478 | load_symbol(kperfdata, kpep_event_alias); 479 | load_symbol(kperfdata, kpep_event_description); 480 | load_symbol(kperfdata, kpep_event_errata); 481 | 482 | loaded = true; 483 | return true; 484 | } 485 | 486 | u32 (*kpc_pmu_version)(void); 487 | int (*kpc_cpu_string)(char *buf, usize buf_size); 488 | int (*kpc_set_counting)(u32 classes); 489 | u32 (*kpc_get_counting)(void); 490 | int (*kpc_set_thread_counting)(u32 classes); 491 | u32 (*kpc_get_thread_counting)(void); 492 | u32 (*kpc_get_config_count)(u32 classes); 493 | u32 (*kpc_get_counter_count)(u32 classes); 494 | int (*kpc_set_config)(u32 classes, u64 *buf_in); 495 | int (*kpc_get_config)(u32 classes, u64 *buf_out); 496 | int (*kpc_get_cpu_counters)(bool all_cpus, u32 classes, int *curcpu, u64 *buf); 497 | int (*kpc_get_thread_counters)(u32 tid, u32 buf_count, u64 *buf); 498 | int (*kpc_force_all_ctrs_set)(int val); 499 | int (*kpc_force_all_ctrs_get)(int *val_out); 500 | int (*kperf_reset)(void); 501 | 502 | int (*kpep_config_create)(kpep_db *db, kpep_config **cfg_ptr); 503 | void (*kpep_config_free)(kpep_config *cfg); 504 | int (*kpep_config_add_event)(kpep_config *cfg, kpep_event **ev_ptr, u32 flag, u32 *err); 505 | int (*kpep_config_remove_event)(kpep_config *cfg, usize idx); 506 | int (*kpep_config_force_counters)(kpep_config *cfg); 507 | int (*kpep_config_events_count)(kpep_config *cfg, usize *count_ptr); 508 | int (*kpep_config_events)(kpep_config *cfg, kpep_event **buf, usize buf_count); 509 | int (*kpep_config_kpc)(kpep_config *cfg, u64 *buf, usize buf_size); 510 | int (*kpep_config_kpc_count)(kpep_config *cfg, usize *count_ptr); 511 | int (*kpep_config_kpc_classes)(kpep_config *cfg, u32 *classes_ptr); 512 | int (*kpep_config_apply)(kpep_config *cfg); 513 | int (*kpep_db_create)(const char *cpu_name, kpep_db **db_ptr); 514 | void (*kpep_db_free)(kpep_db *db); 515 | int (*kpep_db_name)(kpep_db *db, const char **name); 516 | int (*kpep_db_architecture)(kpep_db *db, u32 *arch); 517 | int (*kpep_db_aliases_count)(kpep_db *db, usize *count); 518 | int (*kpep_db_aliases)(kpep_db *db, const char **buf, usize buf_size); 519 | int (*kpep_db_counters_count)(kpep_db *db, u8 classes, usize *count); 520 | int (*kpep_db_events_count)(kpep_db *db, usize *count); 521 | int (*kpep_db_events)(kpep_db *db, kpep_event **buf, usize buf_size); 522 | int (*kpep_db_event)(kpep_db *db, const char *name, kpep_event **ev_ptr); 523 | int (*kpep_event_name)(kpep_event *ev, const char **name_ptr); 524 | int (*kpep_event_alias)(kpep_event *ev, const char **alias_ptr); 525 | int (*kpep_event_description)(kpep_event *ev, const char **str_ptr); 526 | int (*kpep_event_errata)(kpep_event *ev, const char **errata_ptr); 527 | 528 | 529 | 530 | 531 | #include 532 | 533 | // configurable counters count (intel: 4, arm: 8) 534 | #define MAX_COUNTER_NUM 8 535 | 536 | static kpep_db *db = NULL; 537 | 538 | struct yy_perf { 539 | kpep_config *cfg; // nonnull 540 | bool is_opened; 541 | bool is_counting; 542 | const char *names[MAX_COUNTER_NUM]; 543 | u64 counters_begin[MAX_COUNTER_NUM]; 544 | u64 counters_end[MAX_COUNTER_NUM]; 545 | u64 counters_overhead[MAX_COUNTER_NUM]; 546 | u64 counters[MAX_COUNTER_NUM]; 547 | }; 548 | 549 | static bool perf_event_to_name(yy_perf_event ev, 550 | const char **name, const char **alias) { 551 | #if TARGET_CPU_ARM64 552 | switch (ev) { 553 | case YY_PERF_EVENT_CYCLES: 554 | *name = "FIXED_CYCLES"; *alias = "cycles"; return true; 555 | case YY_PERF_EVENT_INSTRUCTIONS: 556 | *name = "FIXED_INSTRUCTIONS"; *alias = "instructions"; return true; 557 | case YY_PERF_EVENT_BRANCHES: 558 | *name = "INST_BRANCH"; *alias = "branches"; return true; 559 | case YY_PERF_EVENT_BRANCH_MISSES: 560 | *name = "BRANCH_MISPREDICT"; *alias = "branch-misses"; return true; 561 | case YY_PERF_EVENT_L1D_LOAD_MISSES: 562 | *name = "DCACHE_LOAD_MISS"; *alias = "L1d-load-misses"; return true; 563 | case YY_PERF_EVENT_L1D_STORE_MISSES: 564 | *name = "DCACHE_STORE_MISS"; *alias = "L1d-store-misses"; return true; 565 | default: 566 | return false; 567 | } 568 | #elif TARGET_CPU_X86 || TARGET_CPU_X86_64 569 | switch (ev) { 570 | case YY_PERF_EVENT_CYCLES: 571 | *name = "CPU_CLK_UNHALTED.THREAD"; *alias = "cycles"; return true; 572 | case YY_PERF_EVENT_INSTRUCTIONS: 573 | *name = "INST_RETIRED.ANY"; *alias = "instructions"; return true; 574 | case YY_PERF_EVENT_BRANCHES: 575 | *name = "BR_INST_RETIRED.ALL_BRANCHES"; *alias = "branches"; return true; 576 | case YY_PERF_EVENT_BRANCH_MISSES: 577 | *name = "BR_MISP_RETIRED.ALL_BRANCHES"; *alias = "branch-misses"; return true; 578 | default: 579 | return false; 580 | } 581 | #else 582 | return false; 583 | #endif 584 | } 585 | 586 | static yy_inline void perf_calc_counters(yy_perf *perf) { 587 | for (i32 i = 0; i < MAX_COUNTER_NUM; i++) { 588 | u64 begin = perf->counters_begin[i]; 589 | u64 end = perf->counters_end[i]; 590 | u64 overhead = perf->counters_overhead[i]; 591 | u64 delta = end - begin; 592 | u64 count = delta < overhead ? 0 : delta - overhead; 593 | perf->counters[i] = count; 594 | } 595 | } 596 | 597 | bool yy_perf_load(bool print_message_on_error) { 598 | bool suc = kperf_load(print_message_on_error); 599 | if (!suc) return false; 600 | if (db) return true; 601 | 602 | int ret = kpep_db_create(NULL, &db); 603 | if (ret != 0) { 604 | if (print_message_on_error) { 605 | fprintf(stderr, "fail to load kpep db for host CPU, error: %d.\n", ret); 606 | } 607 | return false; 608 | } 609 | 610 | u32 ver = kpc_pmu_version(); 611 | if (ver == KPC_PMU_ERROR) { 612 | if (print_message_on_error) { 613 | fprintf(stderr, "Cannot load kperf, this requires root privileges (or blessed).\n"); 614 | } 615 | return false; 616 | } 617 | 618 | return true; 619 | } 620 | 621 | yy_perf *yy_perf_new(void) { 622 | if (!db) return NULL; 623 | 624 | yy_perf *perf = (yy_perf *)calloc(1, sizeof(yy_perf)); 625 | if (!perf) return false; 626 | 627 | int ret = kpep_config_create(db, &perf->cfg); 628 | if (ret != 0) { 629 | free(perf); 630 | return false; 631 | } 632 | return perf; 633 | } 634 | 635 | void yy_perf_free(yy_perf *perf) { 636 | if (!perf) return; 637 | yy_perf_close(perf); 638 | kpep_config_free(perf->cfg); 639 | free(perf); 640 | } 641 | 642 | bool yy_perf_add_event(yy_perf *perf, yy_perf_event event) { 643 | if (!perf) return false; 644 | const char *name, *alias; 645 | if (!perf_event_to_name(event, &name, &alias)) return false; 646 | return yy_perf_add_event_apple(perf, name, alias); 647 | } 648 | 649 | bool yy_perf_remove_all_events(yy_perf *perf) { 650 | if (!perf) return false; 651 | while (true) { 652 | usize count = 0; 653 | if (kpep_config_events_count(perf->cfg, &count) != 0) return false; 654 | if (count == 0) return true; 655 | if (kpep_config_remove_event(perf->cfg, 0) != 0) return false; 656 | } 657 | } 658 | 659 | bool yy_perf_event_available(yy_perf *perf, yy_perf_event ev) { 660 | const char *name, *alias; 661 | if (!perf_event_to_name(ev, &name, &alias)) return false; 662 | return true; 663 | } 664 | 665 | bool yy_perf_add_event_apple(yy_perf *perf, const char *ev_name, const char *ev_alias) { 666 | if (!perf || !ev_name) return false; 667 | 668 | kpep_event *ev; 669 | if (kpep_db_event(db, ev_name, &ev) != 0) return false; 670 | 671 | usize count = 0; 672 | if (kpep_config_events_count(perf->cfg, &count) != 0) return false; 673 | if (kpep_config_add_event(perf->cfg, &ev, 1, NULL) != 0) return false; 674 | perf->names[count] = ev_alias ? ev_alias : ev_name; 675 | return true; 676 | } 677 | 678 | bool yy_perf_event_available_apple(yy_perf *perf, const char *ev_name) { 679 | if (!db || !ev_name) return false; 680 | 681 | kpep_event *ev; 682 | if (kpep_db_event(db, ev_name, &ev) != 0) return false; 683 | return true; 684 | } 685 | 686 | bool yy_perf_add_event_linux(yy_perf *perf, u64 ev_value, const char *ev_alias) { 687 | return false; 688 | } 689 | 690 | bool yy_perf_event_available_linux(yy_perf *perf, u64 ev_value) { 691 | return false; 692 | } 693 | 694 | u32 yy_perf_get_event_count(yy_perf *perf) { 695 | if (!perf) return 0; 696 | usize count = 0; 697 | if (kpep_config_events_count(perf->cfg, &count) != 0) return 0; 698 | return (u32)count; 699 | } 700 | 701 | const char **yy_perf_get_event_names(yy_perf *perf) { 702 | return perf ? perf->names : NULL; 703 | } 704 | 705 | bool yy_perf_open(yy_perf *perf) { 706 | u32 count = yy_perf_get_event_count(perf); 707 | if (count == 0) return false; 708 | 709 | i32 ret; 710 | 711 | u32 classes; 712 | ret = kpep_config_kpc_classes(perf->cfg, &classes); 713 | if (ret != 0) return false; 714 | 715 | ret = kpep_config_apply(perf->cfg); 716 | if (ret != 0) return false; 717 | 718 | ret = kpc_set_counting(classes); 719 | if (ret != 0) return false; 720 | 721 | ret = kpc_set_thread_counting(classes); 722 | if (ret != 0) return false; 723 | 724 | memset(perf->counters_begin, 0, sizeof(perf->counters_begin)); 725 | memset(perf->counters_end, 0, sizeof(perf->counters_end)); 726 | memset(perf->counters_overhead, 0, sizeof(perf->counters_overhead)); 727 | memset(perf->counters, 0, sizeof(perf->counters)); 728 | perf->is_opened = true; 729 | 730 | yy_perf_start_counting(perf); 731 | yy_perf_stop_counting(perf); 732 | memcpy(perf->counters_overhead, yy_perf_get_counters(perf), sizeof(perf->counters)); 733 | 734 | return true; 735 | } 736 | 737 | bool yy_perf_close(yy_perf *perf) { 738 | if (!perf) return false; 739 | i32 ret; 740 | 741 | ret = kpc_set_counting(0); 742 | if (ret != 0) return false; 743 | 744 | ret = kpc_set_thread_counting(0); 745 | if (ret != 0) return false; 746 | 747 | u64 buf[64] = {0}; 748 | ret = kpc_set_config(0, buf); 749 | if (ret != 0) return false; 750 | 751 | return true; 752 | } 753 | 754 | bool yy_perf_is_opened(yy_perf *perf) { 755 | return perf ? perf->is_opened : false; 756 | } 757 | 758 | bool yy_perf_start_counting(yy_perf *perf) { 759 | if (!perf || !perf->is_opened) return false; 760 | if (perf->is_counting) return true; 761 | 762 | memset(perf->counters_begin, 0, sizeof(perf->counters_begin)); 763 | memset(perf->counters_end, 0, sizeof(perf->counters_end)); 764 | memset(perf->counters, 0, sizeof(perf->counters)); 765 | 766 | i32 ret = kpc_get_thread_counters(0, MAX_COUNTER_NUM, perf->counters_begin); 767 | if (ret != 0) return false; 768 | 769 | memcpy(perf->counters_end, perf->counters_begin, MAX_COUNTER_NUM); 770 | 771 | perf->is_counting = true; 772 | return true; 773 | } 774 | 775 | bool yy_perf_stop_counting(yy_perf *perf) { 776 | if (!perf || !perf->is_counting) return false; 777 | 778 | i32 ret = kpc_get_thread_counters(0, MAX_COUNTER_NUM, perf->counters_end); 779 | if (ret != 0) return false; 780 | perf_calc_counters(perf); 781 | perf->is_counting = false; 782 | return true; 783 | } 784 | 785 | bool yy_perf_is_counting(yy_perf *perf) { 786 | return perf ? perf->is_counting : false; 787 | } 788 | 789 | u64 *yy_perf_get_counters(yy_perf *perf) { 790 | if (!perf) return NULL; 791 | 792 | if (perf->is_counting) { 793 | i32 ret = kpc_get_thread_counters(0, MAX_COUNTER_NUM, perf->counters_end); 794 | if (ret != 0) return false; 795 | perf_calc_counters(perf); 796 | } 797 | 798 | return perf->counters; 799 | } 800 | 801 | 802 | #endif 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | /*============================================================================== 812 | * Dummy 813 | *============================================================================*/ 814 | 815 | #if !YY_PERF_AVAILABLE 816 | 817 | struct yy_perf { 818 | int dummy; 819 | }; 820 | 821 | bool yy_perf_load(bool print_message_on_error) { 822 | if (print_message_on_error) { 823 | fprintf(stderr, "perf module does'n support this platform.\n"); 824 | } 825 | return false; 826 | } 827 | 828 | yy_perf *yy_perf_new(void) { 829 | return (yy_perf *)calloc(1, sizeof(yy_perf)); 830 | } 831 | 832 | void yy_perf_free(yy_perf *perf) { 833 | if (perf) free(perf); 834 | } 835 | 836 | bool yy_perf_add_event(yy_perf *perf, yy_perf_event event) { 837 | return false; 838 | } 839 | 840 | bool yy_perf_remove_all_events(yy_perf *perf) { 841 | return false; 842 | } 843 | 844 | bool yy_perf_event_available(yy_perf *perf, yy_perf_event ev) { 845 | return false; 846 | } 847 | 848 | bool yy_perf_add_event_apple(yy_perf *perf, const char *ev_name, const char *ev_alias) { 849 | return false; 850 | } 851 | 852 | bool yy_perf_event_available_apple(yy_perf *perf, const char *ev_name) { 853 | return false; 854 | } 855 | 856 | bool yy_perf_add_event_linux(yy_perf *perf, u64 ev_value, const char *ev_alias) { 857 | return false; 858 | } 859 | 860 | bool yy_perf_event_available_linux(yy_perf *perf, u64 ev_value) { 861 | return false; 862 | } 863 | 864 | u32 yy_perf_get_event_count(yy_perf *perf) { 865 | return 0; 866 | } 867 | 868 | const char **yy_perf_get_event_names(yy_perf *perf) { 869 | return NULL; 870 | } 871 | 872 | bool yy_perf_open(yy_perf *perf) { 873 | return false; 874 | } 875 | 876 | bool yy_perf_close(yy_perf *perf) { 877 | return false; 878 | } 879 | 880 | bool yy_perf_is_opened(yy_perf *perf) { 881 | return false; 882 | } 883 | 884 | bool yy_perf_start_counting(yy_perf *perf) { 885 | return false; 886 | } 887 | 888 | bool yy_perf_stop_counting(yy_perf *perf) { 889 | return false; 890 | } 891 | 892 | bool yy_perf_is_counting(yy_perf *perf) { 893 | return false; 894 | } 895 | 896 | u64 *yy_perf_get_counters(yy_perf *perf) { 897 | return NULL; 898 | } 899 | 900 | #endif 901 | -------------------------------------------------------------------------------- /src/yybench_perf.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_perf_h 7 | #define yybench_perf_h 8 | 9 | #include "yybench_def.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | 16 | /*============================================================================== 17 | Performance Monitor Counter 18 | 19 | Currently only support Linux/Android, macOS/iOS with root privileges. 20 | Do not use it with Intel VTunes, Linux perf, Xcode Instruments Counters, 21 | or it may produce wrong results. 22 | 23 | Notice: A CPU has only a limited number configurable counters. Linux's perf 24 | subsystem use multiplexing to allow us to monitor multiple events, but may 25 | add more overhead. Apple XNU's KPC/kperf module doesn't use multiplexing, 26 | so we can only monitor a limited number of event at the same time. 27 | 28 | Usage: 29 | 30 | // load perf module 31 | bool loaded = yy_perf_load(true); 32 | if (!loaded) return; 33 | 34 | // create perf config, add event 35 | yy_perf *perf = yy_perf_new(); 36 | yy_perf_add_event(perf, YY_PERF_EVENT_CYCLES); 37 | yy_perf_add_event(perf, YY_PERF_EVENT_INSTRUCTIONS); 38 | yy_perf_add_event(perf, YY_PERF_EVENT_BRANCHES); 39 | yy_perf_add_event(perf, YY_PERF_EVENT_BRANCH_MISSES); 40 | 41 | if (!yy_perf_open(perf)) return; 42 | 43 | yy_perf_start_counting(perf); 44 | // code to profile... 45 | yy_perf_stop_counting(perf); 46 | 47 | // get results 48 | u32 ev_count = yy_perf_get_event_count(perf); 49 | const char **names = yy_perf_get_event_names(perf); 50 | u64 *vals = yy_perf_get_counters(perf); 51 | 52 | // print results 53 | for (u32 i = 0; i < ev_count; i++) { 54 | printf("%s: %llu\n",names[i], vals[i]); 55 | } 56 | 57 | // close and free resource 58 | yy_perf_close(perf); 59 | yy_perf_free(perf); 60 | 61 | 62 | *============================================================================*/ 63 | 64 | #define YY_PERF_AVAILABLE 0 65 | #define YY_PERF_AVAILABLE_WINDOWS 0 66 | #define YY_PERF_AVAILABLE_LINUX 0 67 | #define YY_PERF_AVAILABLE_APPLE 0 68 | 69 | 70 | /** Common PMU event */ 71 | typedef enum yy_perf_event { 72 | /* Not a real event, used as placeholder */ 73 | YY_PERF_EVENT_NONE = 0, 74 | 75 | /* CPU cycles count */ 76 | YY_PERF_EVENT_CYCLES, 77 | 78 | /* All retired instruction count */ 79 | YY_PERF_EVENT_INSTRUCTIONS, 80 | 81 | /* Branch instruction count */ 82 | YY_PERF_EVENT_BRANCHES, 83 | 84 | /* Branch instruction mispredict count */ 85 | YY_PERF_EVENT_BRANCH_MISSES, 86 | 87 | /* Level 1 instruction cache load count */ 88 | YY_PERF_EVENT_L1I_LOADS, 89 | 90 | /* Level 1 instruction cache load miss count */ 91 | YY_PERF_EVENT_L1I_LOAD_MISSES, 92 | 93 | /* Level 1 data cache load count */ 94 | YY_PERF_EVENT_L1D_LOADS, 95 | 96 | /* Level 1 data cache load miss count */ 97 | YY_PERF_EVENT_L1D_LOAD_MISSES, 98 | 99 | /* Level 1 data cache store count */ 100 | YY_PERF_EVENT_L1D_STORES, 101 | 102 | /* Level 1 data cache store miss count */ 103 | YY_PERF_EVENT_L1D_STORE_MISSES, 104 | 105 | /* Last-Level Cache load count */ 106 | YY_PERF_EVENT_LLC_LOADS, 107 | 108 | /* Last-Level Cache load miss count */ 109 | YY_PERF_EVENT_LLC_LOAD_MISSES, 110 | 111 | /* Last-Level Cache store count */ 112 | YY_PERF_EVENT_LLC_STORES, 113 | 114 | /* Last-Level Cache store miss count */ 115 | YY_PERF_EVENT_LLC_STORE_MISSES, 116 | } yy_perf_event; 117 | 118 | 119 | /** A perf object. */ 120 | typedef struct yy_perf yy_perf; 121 | 122 | /** 123 | Load perf module. 124 | This function should be called once before any other functions in this file.. 125 | @param print_message_on_error true to print error message on error. 126 | @return true on success. 127 | */ 128 | bool yy_perf_load(bool print_message_on_error); 129 | 130 | /** Creates a perf object. */ 131 | yy_perf *yy_perf_new(void); 132 | 133 | /** Stop and free the perf object. */ 134 | void yy_perf_free(yy_perf *perf); 135 | 136 | /** Add a common event. */ 137 | bool yy_perf_add_event(yy_perf *perf, yy_perf_event event); 138 | 139 | /** Remove all events. */ 140 | bool yy_perf_remove_all_events(yy_perf *perf); 141 | 142 | /** Whether an event is availabie for current host. */ 143 | bool yy_perf_event_available(yy_perf *perf, yy_perf_event ev); 144 | 145 | /** Add an event by name, available for macOS/iOS. 146 | Return false if the event is not supported. 147 | 148 | @param ev_name 149 | You can get event names in file /usr/share/kpep/xxx.plist. 150 | For example: "INST_RETIRED.ANY". 151 | 152 | @param ev_alias 153 | You can give an alias name for this event, or NULL if you don't need it. 154 | */ 155 | bool yy_perf_add_event_apple(yy_perf *perf, const char *ev_name, const char *ev_alias); 156 | 157 | /** Whether an event is availabie for current host. */ 158 | bool yy_perf_event_available_apple(yy_perf *perf, const char *ev_name); 159 | 160 | /** Add an event by type, available for Linux. 161 | Return false if the event is not supported. 162 | 163 | @param ev_value 164 | You can get common names in file , or "man perf_event_open". 165 | For example PERF_EVENT_MAKE(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES). 166 | 167 | You can also use some raw values to build a event. 168 | For example, you can find some raw values in Android project: 169 | https://android.googlesource.com/platform/system/extras/+/master/simpleperf/event_type_table.h 170 | and make event value with: PERF_EVENT_MAKE(PERF_TYPE_RAW, 0x3) 171 | 172 | @param ev_alias 173 | You can give an alias name for this event, or NULL if you don't need it. 174 | */ 175 | bool yy_perf_add_event_linux(yy_perf *perf, u64 ev_value, const char *ev_alias); 176 | 177 | /** Whether an event is availabie for current host. */ 178 | bool yy_perf_event_available_linux(yy_perf *perf, u64 ev_value); 179 | 180 | /** Get current event count. */ 181 | u32 yy_perf_get_event_count(yy_perf *perf); 182 | 183 | /** Get current event names. */ 184 | const char **yy_perf_get_event_names(yy_perf *perf); 185 | 186 | /** Open perf, apply the config to kernel. 187 | After open, you can not add or remove events. */ 188 | bool yy_perf_open(yy_perf *perf); 189 | 190 | /** Close perf (and close counting). */ 191 | bool yy_perf_close(yy_perf *perf); 192 | 193 | /** Whether perf is opened. */ 194 | bool yy_perf_is_opened(yy_perf *perf); 195 | 196 | /** Start perf counting. */ 197 | bool yy_perf_start_counting(yy_perf *perf); 198 | 199 | /** Stop perf counting. */ 200 | bool yy_perf_stop_counting(yy_perf *perf); 201 | 202 | /** Whether perf is counting. */ 203 | bool yy_perf_is_counting(yy_perf *perf); 204 | 205 | /** Get counter values. */ 206 | u64 *yy_perf_get_counters(yy_perf *perf); 207 | 208 | 209 | /*============================================================================== 210 | * Linux 211 | 212 | * Header: 213 | * /usr/include/linux/perf_event.h 214 | * 215 | * Doc 216 | * man perf_event_open 217 | *============================================================================*/ 218 | 219 | #ifndef YY_LINUX_HAS_PERF 220 | # ifdef __linux__ 221 | # if yy_has_include() && \ 222 | (yy_has_include() || \ 223 | yy_has_include("linux/perf_event.h")) 224 | # define YY_LINUX_HAS_PERF 1 225 | # endif 226 | # endif 227 | #endif 228 | 229 | #if YY_LINUX_HAS_PERF 230 | #undef YY_PERF_AVAILABLE 231 | #define YY_PERF_AVAILABLE 1 232 | #undef YY_PERF_AVAILABLE_LINUX 233 | #define YY_PERF_AVAILABLE_LINUX 1 234 | 235 | #include 236 | #include 237 | #include 238 | #include 239 | 240 | /* 241 | Given a list of parameters, returns a file descriptor, 242 | for use in subsequent system calls (ioctl(), read(), close(), etc.). 243 | 244 | @param pid Specifying which process to monitor: 245 | pid = -1: all processes/threads 246 | pid = 0: calling process/thread 247 | pid > 0: the specified process/thread 248 | @param cpu Specifying which CPU to monitor: 249 | cpu = -1: any CPU 250 | cpu >= 0: the specified CPU 251 | @param group_fd Allows event groups to be created. 252 | @param flags See `man perf_event_open` for all available values. 253 | @warning (pid > 0) requires ptrace permission. 254 | (pid = -1 and cpu = -1) is invalid and will return an error. 255 | (pid = -1 and cpu >= 0) requires CAP_SYS_ADMIN capability. 256 | 257 | @see man perf_event_open 258 | */ 259 | static int yy_perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, 260 | int group_fd, unsigned long flags) { 261 | return (int)syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); 262 | } 263 | 264 | #define PERF_EVENT_MAKE(type, config) (((u64)type << 32) | ((u32)config)) 265 | 266 | #define PERF_EVENT_MAKE_CACHE(id, op, result) \ 267 | PERF_EVENT_MAKE(PERF_TYPE_HW_CACHE, ((u32)id | ((u32)op << 8) | ((u32)result << 16))) 268 | 269 | #define PERF_EVENT_GET_TYPE(ev) ((u32)(ev >> 32)) 270 | 271 | #define PERF_EVENT_GET_CONFIG(ev) ((u32)(ev)) 272 | 273 | #endif /* LINUX */ 274 | 275 | 276 | /*============================================================================== 277 | * Apple 278 | *============================================================================*/ 279 | 280 | #ifndef YY_APPLE_HAS_PERF 281 | # if defined(__APPLE__) 282 | # define YY_APPLE_HAS_PERF 1 283 | # endif 284 | #endif 285 | 286 | #if YY_APPLE_HAS_PERF 287 | #undef YY_PERF_AVAILABLE 288 | #define YY_PERF_AVAILABLE 1 289 | #undef YY_PERF_AVAILABLE_APPLE 290 | #define YY_PERF_AVAILABLE_APPLE 1 291 | 292 | 293 | /*============================================================================== 294 | * Apple 295 | * 296 | * Frameworks: 297 | * /System/Library/PrivateFrameworks/kperf.framework 298 | * /System/Library/PrivateFrameworks/kperfdata.framework 299 | * /Applications/Xcode.app/Contents/SharedFrameworks/DVTInstrumentsFoundation.framework 300 | * 301 | * CPU database path: 302 | * /usr/share/kpep/ 303 | * 304 | * Kernel source: https://opensource.apple.com/tarballs/xnu/ 305 | * /xnu/bsd/kern/kern_kpc.c 306 | * /xnu/osfmk/kern/kpc.h 307 | *============================================================================*/ 308 | 309 | 310 | /** 311 | Load kperf and kperfdata dynamic library. 312 | Return true if success. 313 | */ 314 | bool kperf_load(bool print_message_on_error); 315 | 316 | /*============================================================================== 317 | * Apple 318 | * kperf.framework 319 | * Most functions requires root privileges, or process is "blessed". 320 | *============================================================================*/ 321 | 322 | /* cross-platform class constants */ 323 | #define KPC_CLASS_FIXED (0) 324 | #define KPC_CLASS_CONFIGURABLE (1) 325 | #define KPC_CLASS_POWER (2) 326 | #define KPC_CLASS_RAWPMU (3) 327 | 328 | /* cross-platform class mask constants, use these masks as `classes` param */ 329 | #define KPC_CLASS_FIXED_MASK (1u << KPC_CLASS_FIXED) /* 1 */ 330 | #define KPC_CLASS_CONFIGURABLE_MASK (1u << KPC_CLASS_CONFIGURABLE) /* 2 */ 331 | #define KPC_CLASS_POWER_MASK (1u << KPC_CLASS_POWER) /* 4 */ 332 | #define KPC_CLASS_RAWPMU_MASK (1u << KPC_CLASS_RAWPMU) /* 8 */ 333 | 334 | /* PMU version constants */ 335 | #define KPC_PMU_ERROR (0) 336 | #define KPC_PMU_INTEL_V3 (1) 337 | #define KPC_PMU_ARM_APPLE (2) 338 | #define KPC_PMU_INTEL_V2 (3) 339 | #define KPC_PMU_ARM_V2 (4) 340 | 341 | /* Bits to get CPU counters for all CPUs. */ 342 | #define KPC_ALL_CPUS (1u << 31) 343 | 344 | 345 | /// Prints CPU string to the buffer (same as snprintf), such as "cpu_7_8_10b282dc_46". 346 | /// @return string's length, or negative value if error occurs. 347 | /// @note This method does not requires root privileges. 348 | /// @name get(hw.cputype), get(hw.cpusubtype), get(hw.cpufamily), get(machdep.cpu.model) 349 | extern int (*kpc_cpu_string)(char *buf, usize buf_size); 350 | 351 | /// Returns PMU (Performance Monitoring Unit) version for local machine. 352 | /// @return `KPC_PMU_ERROR` if error occurs. 353 | /// @name get(kpc.pmu_version) 354 | extern u32 (*kpc_pmu_version)(void); 355 | 356 | 357 | /// Returns running PMC classes. 358 | /// @return 0 if error occurs or no class is set. 359 | /// @name get(kpc.counting) 360 | extern u32 (*kpc_get_counting)(void); 361 | 362 | /// Sets PMC classes to enable counting, or set 0 to shutdown counting. 363 | /// @return 0 for success. 364 | /// @name set(kpc.counting) 365 | extern int (*kpc_set_counting)(u32 classes); 366 | 367 | /// Returns running PMC classes for current thread. 368 | /// @return 0 if error occurs or no class is set. 369 | /// @name get(kpc.thread_counting) 370 | extern u32 (*kpc_get_thread_counting)(void); 371 | 372 | /// Sets PMC classes to enable counting for current thread, or set 0 to shutdown counting. 373 | /// @return 0 for success. 374 | /// @name set(kpc.thread_counting) 375 | extern int (*kpc_set_thread_counting)(u32 classes); 376 | 377 | 378 | /// Returns how many config registers there are for a given config. 379 | /// For example: Intel may returns 1 for `KPC_CLASS_FIXED_MASK`, 380 | /// returns 4 for `KPC_CLASS_CONFIGURABLE_MASK`. 381 | /// @note This method does not requires root privileges. 382 | /// @name get(kpc.config_count) 383 | extern u32 (*kpc_get_config_count)(u32 classes); 384 | 385 | /// Get counter configs. 386 | /// @param buf_in Config buffer, should not smaller than kpc_get_config_count() 387 | /// @return 0 for success. 388 | /// @name get(kpc.config_count), get(kpc.config) 389 | extern int (*kpc_get_config)(u32 classes, u64 *buf_out); 390 | 391 | /// Set counter configs. 392 | /// @param buf_in Config buffer, should not smaller than kpc_get_config_count() 393 | /// @return 0 for success. 394 | /// @name get(kpc.config_count), set(kpc.config) 395 | extern int (*kpc_set_config)(u32 classes, u64 *buf_in); 396 | 397 | 398 | /// Returns how many counters there are for a given config. 399 | /// For example: Intel may returns 3 for `KPC_CLASS_FIXED_MASK`, 400 | /// returns 4 for `KPC_CLASS_CONFIGURABLE_MASK`. 401 | /// @note This method does not requires root privileges. 402 | /// @name get(kpc.counter_count) 403 | extern u32 (*kpc_get_counter_count)(u32 classes); 404 | 405 | /// Get counter's value. 406 | /// If `all_cpus` is true, the buffer count should not smaller than (cpu_count * counter_count). 407 | /// otherwize, the buffer count should not smaller than (counter_count). 408 | /// @see kpc_get_counter_count(), kpc_cpu_count(). 409 | /// @param all_cpus true for all CPUs, false for current cpu. 410 | /// @param classes The class masks. 411 | /// @param curcpu Output current cpu id, pass NULL if you do not need it. 412 | /// @param buf Buffer to receive counter's value. 413 | /// @return 0 for success. 414 | /// @name get(hw.ncpu), get(kpc.counter_count), get(kpc.counters) 415 | extern int (*kpc_get_cpu_counters)(bool all_cpus, u32 classes, int *curcpu, u64 *buf); 416 | 417 | /// Get counter's value for current thread accumulation. 418 | /// @param tid Thread id, should be 0. 419 | /// @param buf_count The buf's count (not bytes), should not smaller than kpc_get_counter_count(). 420 | /// @param buf Buffer to receive counter's value. 421 | /// @return 0 for success. 422 | /// @name get(kpc.thread_counters) 423 | extern int (*kpc_get_thread_counters)(u32 tid, u32 buf_count, u64 *buf); 424 | 425 | 426 | /// acquire/release the counters used by the Power Manager. 427 | /// @param val 1:acquire, 0:release 428 | /// @return 0 for success. 429 | /// @name set(kpc.force_all_ctrs) 430 | extern int (*kpc_force_all_ctrs_set)(int val); 431 | 432 | /// Get the state of all_ctrs. 433 | /// @return 0 for success. 434 | /// @name get(kpc.force_all_ctrs) 435 | extern int (*kpc_force_all_ctrs_get)(int *val_out); 436 | 437 | /// Reset kperf: stop sampling, kdebug, timers and actions. 438 | /// @return 0 for success. 439 | extern int (*kperf_reset)(void); 440 | 441 | 442 | /*============================================================================== 443 | * kperfdata.framework 444 | *============================================================================*/ 445 | 446 | #define KPEP_ARCH_I386 0 447 | #define KPEP_ARCH_X86_64 1 448 | #define KPEP_ARCH_ARM 2 449 | #define KPEP_ARCH_ARM64 3 450 | 451 | /// kpep event (size: 48/28 bytes on 64/32 bit OS) 452 | typedef struct kpep_event { 453 | const char *name; ///< unique name of a event, such as "INST_RETIRED.ANY" 454 | const char *description; ///< description for this event. 455 | const char *errata; ///< usually NULL 456 | const char *alias; ///< alias name, such as "Instructions", "Cycles" 457 | const char *fallback; ///< fallback event name for fixed counter 458 | u32 mask; 459 | u8 number; 460 | u8 umask; 461 | u8 reserved; 462 | u8 is_fixed; 463 | } kpep_event; 464 | 465 | /// kpep database (size: 144/80 bytes on 64/32 bit OS) 466 | typedef struct kpep_db { 467 | const char *name; ///< database name, such as "haswell" 468 | const char *cpu_id; ///< plist name, such as "cpu_7_8_10b282dc" 469 | const char *marketing_name;///< marketing name, such as "Intel Haswell" 470 | void *plist_data; ///< plist data (CFDataRef), usually NULL 471 | void *event_dict; ///< all event dictionary (CFDictionaryRef ) 472 | kpep_event *events_arr; ///< event struct buffer (sizeof(kpep_event) * events_count) 473 | kpep_event **fixed_events; ///< fixed counter events (sizeof(kpep_event *) * fixed_counter_count) 474 | void *aliases_dic;///< aliases dictionary (CFDictionaryRef ) 475 | usize reserved_1; 476 | usize reserved_2; 477 | usize reserved_3; 478 | usize events_count; ///< all events count 479 | usize aliases_count; 480 | usize fixed_counter_count; 481 | usize config_counter_count; 482 | usize power_counter_count; 483 | u32 archtecture; ///< CPU arch, such as `KPEP_ARCH_X86_64` 484 | u32 fixed_counter_bits; 485 | u32 config_counter_bits; 486 | u32 power_counter_bits; 487 | } kpep_db; 488 | 489 | /// kpep config (size: 80/44 bytes on 64/32 bit OS) 490 | typedef struct kpep_config { 491 | kpep_db *db; 492 | kpep_event **ev_arr; ///< (sizeof(kpep_event *) * counter_count), init NULL 493 | usize *ev_map; ///< (sizeof(usize *) * counter_count), init 0 494 | usize *ev_idx; ///< (sizeof(usize *) * counter_count), init -1 495 | u32 *flags; ///< (sizeof(u32 *) * counter_count), init 0 496 | u64 *kpc_periods; ///< (sizeof(u64 *) * counter_count), init 0 497 | usize events_count; /// kpep_config_events_count() 498 | usize counters_count; 499 | u32 kpc_classes; 500 | u32 config_counter; 501 | u32 power_counter; 502 | u32 reserved; 503 | } kpep_config; 504 | 505 | 506 | /// Create a config. 507 | /// @return 0 for success. 508 | extern int (*kpep_config_create)(kpep_db *db, kpep_config **cfg_ptr); 509 | 510 | /// Free the config. 511 | extern void (*kpep_config_free)(kpep_config *cfg); 512 | 513 | /// Add an event to config. 514 | /// @param cfg The config. 515 | /// @param ev_ptr One event. 516 | /// @param flag Pass 1. 517 | /// @param err Err flag, can be NULL. 518 | /// @return 0: success 519 | /// 1: error trying to add event 520 | /// 12: conflicts with previously added events 521 | /// 13: requires power counters that are not enabled 522 | /// 14: could not locate the event 523 | /// other: unexpected error 524 | extern int (*kpep_config_add_event)(kpep_config *cfg, kpep_event **ev_ptr, u32 flag, u32 *err); 525 | 526 | /// Remove event at index. 527 | /// @return 0 for success. 528 | extern int (*kpep_config_remove_event)(kpep_config *cfg, usize idx); 529 | 530 | /// Force all counters. 531 | /// @return 0 for success. 532 | extern int (*kpep_config_force_counters)(kpep_config *cfg); 533 | 534 | /// Get events count. 535 | /// @return 0 for success. 536 | extern int (*kpep_config_events_count)(kpep_config *cfg, usize *count_ptr); 537 | 538 | /// Get all event pointers. 539 | /// @param buf A buffer to receive event pointers. 540 | /// @param buf_size The buffer's size in bytes. 541 | /// @return 0 for success. 542 | extern int (*kpep_config_events)(kpep_config *cfg, kpep_event **buf, usize buf_size); 543 | 544 | /// Get kpc register config. 545 | /// @param buf A buffer to receive kpc register configs. 546 | /// @param buf_size The buffer's size in bytes. 547 | /// @return 0 for success. 548 | extern int (*kpep_config_kpc)(kpep_config *cfg, u64 *buf, usize buf_size); 549 | 550 | /// Get kpc register config count. 551 | /// @return 0 for success. 552 | extern int (*kpep_config_kpc_count)(kpep_config *cfg, usize *count_ptr); 553 | 554 | /// Get kpc classes. 555 | /// @return 0 for success. 556 | extern int (*kpep_config_kpc_classes)(kpep_config *cfg, u32 *classes_ptr); 557 | 558 | /// Apply config to CPU. 559 | /// @return 0 for success. 560 | /// @see kpep_config_kpc(), kpc_force_all_ctrs_set(1), kpc_set_config() 561 | extern int (*kpep_config_apply)(kpep_config *cfg); 562 | 563 | 564 | /// Create a kpep database. 565 | /// The database file is in "/usr/share/kpep" 566 | /// @param cpu_name CPU name, for example "A12", "haswell". 567 | /// Pass NULL for current CPU. 568 | /// @return 0 for success. 569 | extern int (*kpep_db_create)(const char *cpu_name, kpep_db **db_ptr); 570 | 571 | /// Free the kpep database. 572 | extern void (*kpep_db_free)(kpep_db *db); 573 | 574 | /// Get the database's name. 575 | /// @return 0 for success. 576 | extern int (*kpep_db_name)(kpep_db *db, const char **name); 577 | 578 | /// Get the database's arch. 579 | /// @return 0 for success. 580 | extern int (*kpep_db_architecture)(kpep_db *db, u32 *arch); 581 | 582 | /// Get the event alias count. 583 | /// @return 0 for success. 584 | extern int (*kpep_db_aliases_count)(kpep_db *db, usize *count); 585 | 586 | /// Get all alias. 587 | /// @param buf A buffer to receive all alias strings. 588 | /// @param buf_size The buffer's size in bytes. 589 | /// @return 0 for success. 590 | extern int (*kpep_db_aliases)(kpep_db *db, const char **buf, usize buf_size); 591 | 592 | /// Get counters count for given classes. 593 | /// @return 0 for success. 594 | extern int (*kpep_db_counters_count)(kpep_db *db, u8 classes, usize *count); 595 | 596 | /// Get all event count. 597 | /// @return 0 for success. 598 | extern int (*kpep_db_events_count)(kpep_db *db, usize *count); 599 | 600 | /// Get all events. 601 | /// @param buf A buffer to receive all event pointers. 602 | /// @param buf_size The buffer's size in bytes. 603 | /// @return 0 for success. 604 | extern int (*kpep_db_events)(kpep_db *db, kpep_event **buf, usize buf_size); 605 | 606 | /// Get one event by name. 607 | /// @return 0 for success. 608 | extern int (*kpep_db_event)(kpep_db *db, const char *name, kpep_event **ev_ptr); 609 | 610 | 611 | /// Get event's name. 612 | /// @return 0 for success. 613 | extern int (*kpep_event_name)(kpep_event *ev, const char **name_ptr); 614 | 615 | /// Get event's alias. 616 | /// @return 0 for success. 617 | extern int (*kpep_event_alias)(kpep_event *ev, const char **alias_ptr); 618 | 619 | /// Get event's description. 620 | /// @return 0 for success. 621 | extern int (*kpep_event_description)(kpep_event *ev, const char **str_ptr); 622 | 623 | /// Get event's errata. 624 | /// @return 0 for success. 625 | extern int (*kpep_event_errata)(kpep_event *ev, const char **errata_ptr); 626 | 627 | 628 | #endif /* APPLE */ 629 | 630 | 631 | #ifdef __cplusplus 632 | } 633 | #endif 634 | 635 | #endif 636 | -------------------------------------------------------------------------------- /src/yybench_rand.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench_rand.h" 7 | 8 | 9 | /*============================================================================== 10 | * Random Number Generator 11 | * PCG random: http://www.pcg-random.org 12 | * A fixed seed should be used to ensure repeatability of the test or benchmark. 13 | *============================================================================*/ 14 | 15 | #define YY_RANDOM_STATE_INIT (((u64)0x853C49E6U << 32) + 0x748FEA9BU) 16 | #define YY_RANDOM_INC_INIT (((u64)0xDA3E39CBU << 32) + 0x94B95BDBU) 17 | #define YY_RANDOM_MUL (((u64)0x5851F42DU << 32) + 0x4C957F2DU) 18 | 19 | static u64 yy_random_state = YY_RANDOM_STATE_INIT; 20 | static u64 yy_random_inc = YY_RANDOM_INC_INIT; 21 | 22 | void yy_random_reset(void) { 23 | yy_random_state = YY_RANDOM_STATE_INIT; 24 | yy_random_inc = YY_RANDOM_INC_INIT; 25 | } 26 | 27 | u32 yy_random32(void) { 28 | u32 xorshifted, rot; 29 | u64 oldstate = yy_random_state; 30 | yy_random_state = oldstate * YY_RANDOM_MUL + yy_random_inc; 31 | xorshifted = (u32)(((oldstate >> 18) ^ oldstate) >> 27); 32 | rot = (u32)(oldstate >> 59); 33 | return (xorshifted >> rot) | (xorshifted << (((u32)-(i32)rot) & 31)); 34 | } 35 | 36 | u32 yy_random32_uniform(u32 bound) { 37 | u32 r, threshold = (u32)(-(i32)bound) % bound; 38 | if (bound < 2) return 0; 39 | while (true) { 40 | r = yy_random32(); 41 | if (r >= threshold) return r % bound; 42 | } 43 | } 44 | 45 | u32 yy_random32_range(u32 min, u32 max) { 46 | return yy_random32_uniform(max - min + 1) + min; 47 | } 48 | 49 | u64 yy_random64(void) { 50 | return (u64)yy_random32() << 32 | yy_random32(); 51 | } 52 | 53 | u64 yy_random64_uniform(u64 bound) { 54 | u64 r, threshold = ((u64)-(i64)bound) % bound; 55 | if (bound < 2) return 0; 56 | while (true) { 57 | r = yy_random64(); 58 | if (r >= threshold) return r % bound; 59 | } 60 | } 61 | 62 | u64 yy_random64_range(u64 min, u64 max) { 63 | return yy_random64_uniform(max - min + 1) + min; 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/yybench_rand.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_rand_h 7 | #define yybench_rand_h 8 | 9 | #include "yybench_def.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | 16 | /*============================================================================== 17 | * Random Number Generator 18 | *============================================================================*/ 19 | 20 | /** Reset the random number generator with default seed. */ 21 | void yy_random_reset(void); 22 | 23 | /** Generate a uniformly distributed 32-bit random number. */ 24 | u32 yy_random32(void); 25 | 26 | /** Generate a uniformly distributed number, where 0 <= r < bound. */ 27 | u32 yy_random32_uniform(u32 bound); 28 | 29 | /** Generate a uniformly distributed number, where min <= r <= max. */ 30 | u32 yy_random32_range(u32 min, u32 max); 31 | 32 | /** Generate a uniformly distributed 64-bit random number. */ 33 | u64 yy_random64(void); 34 | 35 | /** Generate a uniformly distributed number, where 0 <= r < bound. */ 36 | u64 yy_random64_uniform(u64 bound); 37 | 38 | /** Generate a uniformly distributed number, where min <= r <= max. */ 39 | u64 yy_random64_range(u64 min, u64 max); 40 | 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/yybench_str.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench_str.h" 7 | 8 | 9 | /*============================================================================== 10 | * String Utils 11 | *============================================================================*/ 12 | 13 | char *yy_str_copy(const char *str) { 14 | if (!str) return NULL; 15 | usize len = strlen(str) + 1; 16 | char *dup = malloc(len); 17 | if (dup) memcpy(dup, str, len); 18 | return dup; 19 | } 20 | 21 | bool yy_str_contains(const char *str, const char *search) { 22 | if (!str || !search) return false; 23 | return strstr(str, search) != NULL; 24 | } 25 | 26 | bool yy_str_has_prefix(const char *str, const char *prefix) { 27 | if (!str || !prefix) return false; 28 | usize len1 = strlen(str); 29 | usize len2 = strlen(prefix); 30 | if (len2 > len1) return false; 31 | return memcmp(str, prefix, len2) == 0; 32 | } 33 | 34 | bool yy_str_has_suffix(const char *str, const char *suffix) { 35 | if (!str || !suffix) return false; 36 | usize len1 = strlen(str); 37 | usize len2 = strlen(suffix); 38 | if (len2 > len1) return false; 39 | return memcmp(str + (len1 - len2), suffix, len2) == 0; 40 | } 41 | 42 | 43 | 44 | /*============================================================================== 45 | * Memory Buffer 46 | *============================================================================*/ 47 | 48 | bool yy_buf_init(yy_buf *buf, usize len) { 49 | if (!buf) return false; 50 | if (len < 16) len = 16; 51 | memset(buf, 0, sizeof(yy_buf)); 52 | buf->hdr = malloc(len); 53 | if (!buf->hdr) return false; 54 | buf->cur = buf->hdr; 55 | buf->end = buf->hdr + len; 56 | buf->need_free = true; 57 | return true; 58 | } 59 | 60 | void yy_buf_release(yy_buf *buf) { 61 | if (!buf || !buf->hdr) return; 62 | if (buf->need_free) free(buf->hdr); 63 | memset(buf, 0, sizeof(yy_buf)); 64 | } 65 | 66 | usize yy_buf_len(yy_buf *buf) { 67 | if (!buf) return 0; 68 | return buf->cur - buf->hdr; 69 | } 70 | 71 | bool yy_buf_grow(yy_buf *buf, usize len) { 72 | if (!buf) return false; 73 | if ((usize)(buf->end - buf->cur) >= len) return true; 74 | if (!buf->hdr) return yy_buf_init(buf, len); 75 | 76 | usize use = buf->cur - buf->hdr; 77 | usize alc = buf->end - buf->hdr; 78 | do { 79 | if (alc * 2 < alc) return false; /* overflow */ 80 | alc *= 2; 81 | } while(alc - use < len); 82 | u8 *tmp = (u8 *)realloc(buf->hdr, alc); 83 | if (!tmp) return false; 84 | 85 | buf->cur = tmp + (buf->cur - buf->hdr); 86 | buf->hdr = tmp; 87 | buf->end = tmp + alc; 88 | return true; 89 | } 90 | 91 | bool yy_buf_append(yy_buf *buf, u8 *dat, usize len) { 92 | if (!buf) return false; 93 | if (len == 0) return true; 94 | if (!dat) return false; 95 | if (!yy_buf_grow(buf, len)) return false; 96 | memcpy(buf->cur, dat, len); 97 | buf->cur += len; 98 | return true; 99 | } 100 | 101 | 102 | 103 | /*============================================================================== 104 | * String Builder 105 | *============================================================================*/ 106 | 107 | bool yy_sb_init(yy_sb *sb, usize len) { 108 | return yy_buf_init(sb, len); 109 | } 110 | 111 | void yy_sb_release(yy_sb *sb) { 112 | yy_buf_release(sb); 113 | } 114 | 115 | usize yy_sb_get_len(yy_sb *sb) { 116 | if (!sb) return 0; 117 | return sb->cur - sb->hdr; 118 | } 119 | 120 | char *yy_sb_get_str(yy_sb *sb) { 121 | if (!sb || !sb->hdr) return NULL; 122 | if (sb->cur >= sb->end) { 123 | if (!yy_buf_grow(sb, 1)) return NULL; 124 | } 125 | *sb->cur = '\0'; 126 | return (char *)sb->hdr; 127 | } 128 | 129 | char *yy_sb_copy_str(yy_sb *sb, usize *len) { 130 | if (!sb || !sb->hdr) return NULL; 131 | usize sb_len = sb->cur - sb->hdr; 132 | char *str = (char *)malloc(sb_len + 1); 133 | if (!str) return NULL; 134 | memcpy(str, sb->hdr, sb_len); 135 | str[sb_len] = '\0'; 136 | if (len) *len = sb_len; 137 | return str; 138 | } 139 | 140 | bool yy_sb_append(yy_sb *sb, const char *str) { 141 | if (!str) return false; 142 | usize len = strlen(str); 143 | if (!yy_buf_grow(sb, len + 1)) return false; 144 | memcpy(sb->cur, str, len); 145 | sb->cur += len; 146 | return true; 147 | } 148 | 149 | bool yy_sb_append_html(yy_sb *sb, const char *str) { 150 | if (!sb || !str) return false; 151 | const char *cur = str; 152 | usize hdr_pos = sb->cur - sb->hdr; 153 | while (true) { 154 | usize esc_len; 155 | const char *esc; 156 | if (*cur == '\0') break; 157 | switch (*cur) { 158 | case '"': esc = """; esc_len = 6; break; 159 | case '&': esc = "&"; esc_len = 5; break; 160 | case '\'': esc = "'"; esc_len = 5; break; 161 | case '<': esc = "<"; esc_len = 4; break; 162 | case '>': esc = ">"; esc_len = 4; break; 163 | default: esc = NULL; esc_len = 0; break; 164 | } 165 | if (esc_len) { 166 | usize len = cur - str; 167 | if (!yy_buf_grow(sb, len + esc_len + 1)) { 168 | sb->cur = sb->hdr + hdr_pos; 169 | return false; 170 | } 171 | memcpy(sb->cur, str, len); 172 | sb->cur += len; 173 | memcpy(sb->cur, esc, esc_len); 174 | sb->cur += esc_len; 175 | str = cur + 1; 176 | } 177 | cur++; 178 | } 179 | if (cur != str) { 180 | if (!yy_sb_append(sb, str)) { 181 | sb->cur = sb->hdr + hdr_pos; 182 | return false; 183 | } 184 | } 185 | return true; 186 | } 187 | 188 | bool yy_sb_append_esc(yy_sb *sb, char esc, const char *str) { 189 | if (!sb || !str) return false; 190 | const char *cur = str; 191 | usize hdr_pos = sb->cur - sb->hdr; 192 | while (true) { 193 | char c = *cur; 194 | if (c == '\0') break; 195 | if (c == '\\') { 196 | if (*(cur++) == '\0') break; 197 | else continue; 198 | } 199 | if (c == esc) { 200 | usize len = cur - str + 2; 201 | if (!yy_buf_grow(sb, len + 1)) { 202 | sb->cur = sb->hdr + hdr_pos; 203 | return false; 204 | } 205 | memcpy(sb->cur, str, len - 2); 206 | sb->cur[len - 2] = '\\'; 207 | sb->cur[len - 1] = esc; 208 | sb->cur += len; 209 | str = cur + 1; 210 | } 211 | cur++; 212 | } 213 | if (cur != str) { 214 | if (!yy_sb_append(sb, str)) { 215 | sb->cur = sb->hdr + hdr_pos; 216 | return false; 217 | } 218 | } 219 | return true; 220 | } 221 | 222 | bool yy_sb_printf(yy_sb *sb, const char *fmt, ...) { 223 | if (!sb || !fmt) return false; 224 | usize incr_size = 64; 225 | int len = 0; 226 | do { 227 | if (!yy_buf_grow(sb, incr_size + 1)) return false; 228 | va_list args; 229 | va_start(args, fmt); 230 | len = vsnprintf((char *)sb->cur, incr_size, fmt, args); 231 | va_end(args); 232 | if (len < 0) return false; /* error */ 233 | if ((usize)len < incr_size) break; /* success */ 234 | if (incr_size * 2 < incr_size) return false; /* overflow */ 235 | incr_size *= 2; 236 | } while (true); 237 | sb->cur += len; 238 | return true; 239 | } 240 | 241 | 242 | -------------------------------------------------------------------------------- /src/yybench_str.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_str_h 7 | #define yybench_str_h 8 | 9 | #include "yybench_def.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | 16 | /*============================================================================== 17 | * String Utils 18 | *============================================================================*/ 19 | 20 | /** Copy a string, same as strdup(). */ 21 | char *yy_str_copy(const char *str); 22 | 23 | /** Returns whether the string contains a given string. */ 24 | bool yy_str_contains(const char *str, const char *search); 25 | 26 | /** Returns whether the string begins with a prefix. */ 27 | bool yy_str_has_prefix(const char *str, const char *prefix); 28 | 29 | /** Returns whether the string ends with a suffix. */ 30 | bool yy_str_has_suffix(const char *str, const char *suffix); 31 | 32 | 33 | /*============================================================================== 34 | * Memory Buffer 35 | *============================================================================*/ 36 | 37 | /** A memory buffer s*/ 38 | typedef struct yy_buf { 39 | u8 *cur; /* cursor between hdr and end */ 40 | u8 *hdr; /* head of the buffer */ 41 | u8 *end; /* tail of the buffer */ 42 | bool need_free; 43 | } yy_buf; 44 | 45 | /** Initialize a memory buffer with length. */ 46 | bool yy_buf_init(yy_buf *buf, usize len); 47 | 48 | /** Release the memory in buffer. */ 49 | void yy_buf_release(yy_buf *buf); 50 | 51 | /** Returns the used length of buffer (cur - hdr). */ 52 | usize yy_buf_len(yy_buf *buf); 53 | 54 | /** Increase memory buffer and let (end - cur >= len). */ 55 | bool yy_buf_grow(yy_buf *buf, usize len); 56 | 57 | /** Append data to buffer and move cursor. */ 58 | bool yy_buf_append(yy_buf *buf, u8 *dat, usize len); 59 | 60 | 61 | /*============================================================================== 62 | * String Builder 63 | *============================================================================*/ 64 | 65 | /** A string builder */ 66 | typedef struct yy_buf yy_sb; 67 | 68 | /** Initialize a string builder with capacity. */ 69 | bool yy_sb_init(yy_sb *buf, usize len); 70 | 71 | /** Release the string builder. */ 72 | void yy_sb_release(yy_sb *buf); 73 | 74 | /** Returns the length of string. */ 75 | usize yy_sb_get_len(yy_sb *sb); 76 | 77 | /** Returns the inner string */ 78 | char *yy_sb_get_str(yy_sb *sb); 79 | 80 | /** Copies and returns the string, should be released with free(). */ 81 | char *yy_sb_copy_str(yy_sb *sb, usize *len); 82 | 83 | /** Append string. */ 84 | bool yy_sb_append(yy_sb *sb, const char *str); 85 | 86 | /** Append string and escape html. */ 87 | bool yy_sb_append_html(yy_sb *sb, const char *str); 88 | 89 | /** Append string and escape single character. */ 90 | bool yy_sb_append_esc(yy_sb *sb, char esc, const char *str); 91 | 92 | /** Append string with format. */ 93 | bool yy_sb_printf(yy_sb *sb, const char *fmt, ...); 94 | 95 | 96 | #ifdef __cplusplus 97 | } 98 | #endif 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/yybench_time.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench_time.h" 7 | -------------------------------------------------------------------------------- /src/yybench_time.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #ifndef yybench_time_h 7 | #define yybench_time_h 8 | 9 | #include "yybench_def.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | 16 | /*============================================================================== 17 | * Timer 18 | *============================================================================*/ 19 | 20 | /** Structure holding a timestamp. */ 21 | typedef struct yy_time yy_time; 22 | 23 | /** Get current wall time to a structure. */ 24 | static yy_inline void yy_time_get_current(yy_time *t); 25 | 26 | /** Convert time structure to seconds. */ 27 | static yy_inline f64 yy_time_to_seconds(yy_time *t); 28 | 29 | /** Get current wall time in seconds. */ 30 | static yy_inline f64 yy_time_get_seconds(void); 31 | 32 | /** A high-resolution, low-overhead, fixed-frequency timer for benchmark. */ 33 | static yy_inline u64 yy_time_get_ticks(void); 34 | 35 | 36 | /*============================================================================== 37 | * Timer (Private) 38 | *============================================================================*/ 39 | 40 | #if defined(_WIN32) 41 | 42 | struct yy_time { 43 | LARGE_INTEGER counter; 44 | }; 45 | 46 | static yy_inline void yy_time_get_current(yy_time *t) { 47 | QueryPerformanceCounter(&t->counter); 48 | } 49 | 50 | static yy_inline f64 yy_time_to_seconds(yy_time *t) { 51 | LARGE_INTEGER freq; 52 | QueryPerformanceFrequency(&freq); 53 | return (f64)t->counter.QuadPart / (f64)freq.QuadPart; 54 | } 55 | 56 | #else 57 | 58 | struct yy_time { 59 | struct timeval now; 60 | }; 61 | 62 | static yy_inline void yy_time_get_current(yy_time *t) { 63 | gettimeofday(&t->now, NULL); 64 | } 65 | 66 | static yy_inline f64 yy_time_to_seconds(yy_time *t) { 67 | return (f64)t->now.tv_sec + (f64)t->now.tv_usec / 1000.0 / 1000.0; 68 | } 69 | 70 | #endif 71 | 72 | static yy_inline f64 yy_time_get_seconds(void) { 73 | yy_time t; 74 | yy_time_get_current(&t); 75 | return yy_time_to_seconds(&t); 76 | } 77 | 78 | #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64)) 79 | # pragma intrinsic(__rdtsc) 80 | #endif 81 | 82 | static yy_inline u64 yy_time_get_ticks() { 83 | /* 84 | RDTSC is a fixed-frequency timer on modern x86 CPU, 85 | and may not match to CPU clock cycles. 86 | */ 87 | #if defined(_WIN32) 88 | # if defined(_M_IX86) || defined(_M_AMD64) 89 | return __rdtsc(); 90 | # else 91 | LARGE_INTEGER now; 92 | QueryPerformanceCounter(&now); 93 | return (u64)now.QuadPart; 94 | # endif 95 | 96 | #elif defined(__i386__) || defined(__i386) 97 | u64 tsc; 98 | __asm volatile("rdtsc" : "=a"(tsc)); 99 | return tsc; 100 | 101 | #elif defined(__x86_64__) || defined(__x86_64) || \ 102 | defined(__amd64__) || defined(__amd64) 103 | u64 lo, hi; 104 | __asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); 105 | return (hi << 32u) | lo; 106 | 107 | #elif defined(__ia64__) 108 | u64 tsc; 109 | __asm volatile("mov %0 = ar.itc" : "=r"(tsc)); 110 | return tsc; 111 | 112 | /* 113 | We can use several counter registers in the ARM CPU: 114 | 115 | CNTPCT_EL0: physical counter 116 | CNTVCT_EL0: virtual counter (physical counter - offset) 117 | PMCCNTR_EL0: performance monitors control cycle count register 118 | 119 | The physical counter (and virtual counter) runs at a fixed frequency which 120 | is different with the CPU cycle rate. For example: Apple A10 max clock rate 121 | is 2.34GHz, but the physical counter frequency is fixed to 24MHz. 122 | 123 | Some of these registers are optional, or may be disabled in user mode. 124 | For example: read CNTVCT_EL0 or PMCCNTR_EL0 in iPhone may get 125 | 'EXC_BAD_INSTRUCTION' exception. 126 | */ 127 | #elif defined(__aarch64__) 128 | u64 tsc; 129 | # if defined(__APPLE__) 130 | /* used by mach_absolute_time(), see mach_absolute_time.s */ 131 | __asm volatile("mrs %0, cntpct_el0" : "=r"(tsc)); 132 | # else 133 | __asm volatile("mrs %0, cntvct_el0" : "=r"(tsc)); 134 | # endif 135 | return tsc; 136 | 137 | #elif defined(__ARM_ARCH) && defined(__APPLE__) 138 | return mach_absolute_time(); 139 | 140 | #else 141 | struct timeval now; 142 | gettimeofday(&now, NULL); 143 | return (u64)now.tv_sec * 1000000 + now.tv_usec; 144 | #endif 145 | } 146 | 147 | 148 | #ifdef __cplusplus 149 | } 150 | #endif 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /test/yybench_test.c: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * Copyright (C) 2020 YaoYuan . 3 | * Released under the MIT license (MIT). 4 | *============================================================================*/ 5 | 6 | #include "yybench.h" 7 | 8 | 9 | static void test_env(void) { 10 | printf("prepare...\n"); 11 | yy_cpu_measure_freq(); 12 | printf("OS: %s\n", yy_env_get_os_desc()); 13 | printf("Compiler: %s\n", yy_env_get_compiler_desc()); 14 | printf("CPU: %s\n", yy_env_get_cpu_desc()); 15 | printf("CPU Freq: %.2f MHz\n", yy_cpu_get_freq() / 1000.0 / 1000.0); 16 | printf("\n"); 17 | } 18 | 19 | 20 | static void test_perf(void) { 21 | printf("pref test:\n"); 22 | if (yy_perf_load(true) == false) return; 23 | 24 | // create perf object 25 | yy_perf *perf = yy_perf_new(); 26 | 27 | // add event and open perf 28 | yy_perf_add_event(perf, YY_PERF_EVENT_CYCLES); 29 | yy_perf_add_event(perf, YY_PERF_EVENT_INSTRUCTIONS); 30 | yy_perf_add_event(perf, YY_PERF_EVENT_BRANCHES); 31 | yy_perf_add_event(perf, YY_PERF_EVENT_BRANCH_MISSES); 32 | if (!yy_perf_open(perf)) { 33 | printf("perf open fail\n"); 34 | return; 35 | } 36 | 37 | // profile 38 | yy_perf_start_counting(perf); 39 | u64 t1 = yy_time_get_ticks(); 40 | volatile int add = 0; 41 | for (int i = 0; i < 100000000; i++) { 42 | add++; 43 | } 44 | u64 t2 = yy_time_get_ticks(); 45 | yy_perf_stop_counting(perf); 46 | 47 | // get result 48 | u32 count = yy_perf_get_event_count(perf); 49 | const char **names = yy_perf_get_event_names(perf); 50 | u64 *counters = yy_perf_get_counters(perf); 51 | 52 | for (u32 i = 0; i < count; i++) { 53 | printf("%d. %.14s: %llu\n",i, names[i], counters[i]); 54 | } 55 | 56 | // tick 57 | u64 tick = t2 - t1; 58 | f64 time = yy_cpu_tick_to_sec(tick); 59 | printf("time: %.3f ms\n", time * 1000.0); 60 | printf("Cycles: %llu(PMU), %llu(Tick), accuracy:%.3f%%\n", 61 | counters[0], yy_cpu_tick_to_cycle(tick), 62 | (f64)yy_cpu_tick_to_cycle(tick) / counters[0] * 100); 63 | printf("IPC: %.3f\n", (f64)counters[1] / (f64)counters[0]); 64 | 65 | // close perf and free resources 66 | yy_perf_close(perf); 67 | yy_perf_free(perf); 68 | 69 | printf("\n"); 70 | } 71 | 72 | 73 | static void test_chart(void) { 74 | // Create a report, add some infos. 75 | yy_report *report = yy_report_new(); 76 | yy_report_add_info(report, "This is a report demo"); 77 | yy_report_add_info(report, "The chart is rendered with highcharts"); 78 | yy_report_add_env_info(report); 79 | 80 | { 81 | // Config line chart options. 82 | yy_chart_options op; 83 | yy_chart_options_init(&op); 84 | op.title = "Line Chart Demo"; 85 | op.type = YY_CHART_LINE; 86 | op.v_axis.title = "this is v axis"; 87 | op.h_axis.title = "this is h axis"; 88 | op.tooltip.value_decimals = 3; 89 | 90 | // Create a chart and set options. 91 | yy_chart *chart = yy_chart_new(); 92 | yy_chart_set_options(chart, &op); 93 | 94 | // Add a line to chart. 95 | yy_chart_item_begin(chart, "sin line"); 96 | for (float i = 0; i < M_PI * 2; i += 0.1f) { 97 | yy_chart_item_add_float(chart, sinf(i)); 98 | } 99 | yy_chart_item_end(chart); 100 | 101 | // Add a line to chart. 102 | yy_chart_item_begin(chart, "cos line"); 103 | for (float i = 0; i < M_PI * 2; i += 0.1f) { 104 | yy_chart_item_add_float(chart, cosf(i)); 105 | } 106 | yy_chart_item_end(chart); 107 | 108 | // Add chart to report, and free the chart. 109 | yy_report_add_chart(report, chart); 110 | yy_chart_free(chart); 111 | } 112 | 113 | { 114 | // Config bar chart options. 115 | yy_chart_options op; 116 | yy_chart_options_init(&op); 117 | op.title = "Bar Chart Demo"; 118 | op.type = YY_CHART_BAR; 119 | op.v_axis.title = "this is v axis"; 120 | op.h_axis.title = "this is h axis"; 121 | const char *categories[] = {"Q1", "Q2", "Q3", "Q4", NULL};; 122 | op.v_axis.categories = categories; 123 | 124 | // Create a chart and set options. 125 | yy_chart *chart = yy_chart_new(); 126 | yy_chart_set_options(chart, &op); 127 | 128 | // Add a bar group to chart. 129 | yy_chart_item_begin(chart, "year 2019"); 130 | yy_chart_item_add_int(chart, 20); 131 | yy_chart_item_add_int(chart, 25); 132 | yy_chart_item_add_int(chart, 30); 133 | yy_chart_item_add_int(chart, 15); 134 | yy_chart_item_end(chart); 135 | 136 | // Add a bar group to chart. 137 | yy_chart_item_begin(chart, "year 2020"); 138 | yy_chart_item_add_int(chart, 20); 139 | yy_chart_item_add_int(chart, 30); 140 | yy_chart_item_add_int(chart, 45); 141 | yy_chart_item_add_int(chart, 25); 142 | yy_chart_item_end(chart); 143 | 144 | // Add chart to report, and free the chart. 145 | yy_report_add_chart(report, chart); 146 | yy_chart_free(chart); 147 | } 148 | 149 | { 150 | // Config table options. 151 | yy_chart_options op; 152 | yy_chart_options_init(&op); 153 | op.title = "Sortable Table Demo"; 154 | op.type = YY_CHART_TABLE; 155 | static const char *categories[] = {"Q1", "Q2", "Q3", "Q4", NULL};; 156 | op.h_axis.categories = categories; 157 | 158 | // Create a chart and set options. 159 | yy_chart *chart = yy_chart_new(); 160 | yy_chart_set_options(chart, &op); 161 | 162 | // Add a line to table. 163 | yy_chart_item_begin(chart, "year 2018"); 164 | yy_chart_item_add_int(chart, 10); 165 | yy_chart_item_add_int(chart, 10); 166 | yy_chart_item_add_int(chart, 10); 167 | yy_chart_item_add_int(chart, 10); 168 | yy_chart_item_end(chart); 169 | 170 | // Add a line to table. 171 | yy_chart_item_begin(chart, "year 2019"); 172 | yy_chart_item_add_int(chart, 20); 173 | yy_chart_item_add_int(chart, 25); 174 | yy_chart_item_add_int(chart, 30); 175 | yy_chart_item_add_int(chart, 15); 176 | yy_chart_item_end(chart); 177 | 178 | // Add a line to table. 179 | yy_chart_item_begin(chart, "year 2020"); 180 | yy_chart_item_add_int(chart, 20); 181 | yy_chart_item_add_int(chart, 30); 182 | yy_chart_item_add_int(chart, 45); 183 | yy_chart_item_add_int(chart, 25); 184 | yy_chart_item_end(chart); 185 | 186 | // Add chart to report, and free the chart. 187 | yy_report_add_chart(report, chart); 188 | yy_chart_free(chart); 189 | } 190 | 191 | 192 | if (yy_perf_load(false)) { 193 | // branch misprediction penalty 194 | printf("test branch misprediction penalty...\n"); 195 | 196 | #define ITERAT_NUM 128 197 | #define SAMPLE_NUM 200 198 | #define BRANCH_NUM 4096 199 | float cycles[SAMPLE_NUM + 1] = {0}; 200 | float misses[SAMPLE_NUM + 1] = {0}; 201 | 202 | yy_perf *perf = yy_perf_new(); 203 | yy_perf_add_event(perf, YY_PERF_EVENT_CYCLES); 204 | yy_perf_add_event(perf, YY_PERF_EVENT_BRANCH_MISSES); 205 | yy_perf_open(perf); 206 | for (int iter = 0; iter < ITERAT_NUM; iter++) { 207 | for (int s = 0; s <= SAMPLE_NUM; s++) { 208 | yy_perf_start_counting(perf); 209 | for (int i = 0; i < BRANCH_NUM; i++) { 210 | if ((int)(yy_random32() % SAMPLE_NUM) < s) { 211 | yy_random32(); 212 | yy_random32(); 213 | yy_random32(); 214 | yy_random32(); 215 | } else { 216 | yy_random64(); 217 | yy_random64(); 218 | } 219 | } 220 | yy_perf_stop_counting(perf); 221 | 222 | u64 *vals = yy_perf_get_counters(perf); 223 | u64 cycle = vals[0]; 224 | u64 miss = vals[1]; 225 | cycles[s] += cycle; 226 | misses[s] += miss; 227 | } 228 | } 229 | float cycle_min = cycles[0]; 230 | float cycle_max = cycles[SAMPLE_NUM]; 231 | for (int s = 0; s <= SAMPLE_NUM; s++) { 232 | cycles[s] -= cycle_min + (cycle_max - cycle_min) * s / SAMPLE_NUM; 233 | cycles[s] /= BRANCH_NUM * ITERAT_NUM; 234 | misses[s] /= BRANCH_NUM * ITERAT_NUM; 235 | } 236 | yy_perf_free(perf); 237 | 238 | 239 | // Config line chart options. 240 | yy_chart_options op; 241 | yy_chart_options_init(&op); 242 | op.width = 600; 243 | op.height = 400; 244 | op.title = "CPU Branch Misprediction Penalty"; 245 | op.type = YY_CHART_LINE; 246 | op.h_axis.title = "random"; 247 | op.tooltip.value_decimals = 3; 248 | 249 | // Create a chart and set options. 250 | yy_chart *chart = yy_chart_new(); 251 | yy_chart_set_options(chart, &op); 252 | 253 | yy_chart_item_begin(chart, "cycles"); 254 | for (int i = 0; i <= SAMPLE_NUM; i ++) { 255 | yy_chart_item_add_float(chart, cycles[i]); 256 | } 257 | yy_chart_item_end(chart); 258 | 259 | yy_chart_item_begin(chart, "miss rate"); 260 | for (int i = 0; i <= SAMPLE_NUM; i ++) { 261 | yy_chart_item_add_float(chart, misses[i]); 262 | } 263 | yy_chart_item_end(chart); 264 | 265 | yy_chart_item_begin(chart, "penalty"); 266 | for (int i = 0; i <= SAMPLE_NUM; i ++) { 267 | float cycle = cycles[i]; 268 | float miss = misses[i]; 269 | float penalty = cycle / miss; 270 | if (!isfinite(penalty)) penalty = 0; 271 | penalty = penalty < 0 ? 0 : penalty > 50 ? 50 : penalty; 272 | yy_chart_item_add_float(chart, penalty); 273 | } 274 | yy_chart_item_end(chart); 275 | 276 | 277 | // Add chart to report, and free the chart. 278 | yy_report_add_chart(report, chart); 279 | yy_chart_free(chart); 280 | 281 | 282 | 283 | } 284 | 285 | 286 | // Write and free the report 287 | const char *path = "report.html"; 288 | yy_report_write_html_file(report, path); 289 | yy_report_free(report); 290 | printf("write demo chart report to: %s\n", path); 291 | } 292 | 293 | 294 | int main(void) { 295 | test_env(); 296 | test_perf(); 297 | test_chart(); 298 | } 299 | --------------------------------------------------------------------------------