1 /* EINA - EFL data type library 2 * Copyright (C) 2008 Cedric Bail 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; 16 * if not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #ifndef EINA_BENCHMARK_H_ 20 #define EINA_BENCHMARK_H_ 21 22 #include "eina_array.h" 23 24 25 26 /** 27 * @page tutorial_benchmark_page Benchmark Tutorial 28 * 29 * The Benchmark module allows you to write easily benchmarks 30 * framework to a project for timing critical parts and detecting slow 31 * parts of code. In addition it automatically creates data files of 32 * these benchmarks, as well as a gnuplot file which can display the 33 * comparison curves of the benchmarks. 34 * 35 * @section tutorial_benchmark_basic_usage Basic Usage 36 * 37 * To create a basic benchmark, you have to follow these steps: 38 * 39 * @li Create a new benchmark 40 * @li Write the functions that wraps the functions you want to 41 * benchmark. 42 * @li Register these wrappers functions. 43 * @li Run the benchmark. 44 * @li Free the memory. 45 * 46 * Here is a basic example of benchmark which creates two functions 47 * that will be run. These functions just print a message. 48 * 49 * @code 50 * #include <stdlib.h> 51 * #include <stdio.h> 52 * 53 * #include <Eina.h> 54 * 55 * static 56 * void work1(int request) 57 * { 58 * printf ("work1 in progress... Request: %d\n", request); 59 * } 60 * 61 * static 62 * void work2(int request) 63 * { 64 * printf ("work2 in progress... Request: %d\n", request); 65 * } 66 * 67 * int main() 68 * { 69 * Eina_Benchmark *test; 70 * Eina_Array *ea; 71 * 72 * if (!eina_init()) 73 * return EXIT_FAILURE; 74 * 75 * test = eina_benchmark_new("test", "run"); 76 * if (!test) 77 * goto shutdown_eina; 78 * 79 * eina_benchmark_register(test, "work-1", EINA_BENCHMARK(work1), 200, 300, 10); 80 * eina_benchmark_register(test, "work-2", EINA_BENCHMARK(work2), 100, 150, 5); 81 * 82 * ea = eina_benchmark_run(test); 83 * 84 * eina_benchmark_free(test); 85 * eina_shutdown(); 86 * 87 * return EXIT_SUCCESS; 88 * 89 * shutdown_eina: 90 * eina_shutdown(); 91 * 92 * return EXIT_FAILURE; 93 * } 94 * @endcode 95 * 96 * As "test", "run" are passed to eina_benchmark_new() and as the tests 97 * "work-1" and "work-2" are registered, the data files 98 * bench_test_run.work-1.data and bench_test_run.work-2.data will be 99 * created after the eina_benchmark_run() call. They contain four 100 * columns. The file bench_test_run.work-1.data contains for example: 101 * 102 * @code 103 * # specimen experiment time starting time ending time 104 * 200 23632 2852446 2876078 105 * 210 6924 2883046 2889970 106 * 220 6467 2895962 2902429 107 * 230 6508 2908271 2914779 108 * 240 6278 2920610 2926888 109 * 250 6342 2932830 2939172 110 * 260 6252 2944954 2951206 111 * 270 6463 2956978 2963441 112 * 280 6347 2969548 2975895 113 * 290 6457 2981702 2988159 114 * @endcode 115 * 116 * The first column (specimen) is the integer passed to the work1() 117 * function when the test is run. The second column (experiment time) 118 * is the time, in nanosecond, that work1() takes. The third and 119 * fourth columns are self-explicit. 120 * 121 * You can see that the integer passed work1() starts from 200 and 122 * finishes at 290, with a step of 10. These values are computed withe 123 * last 3 values passed to eina_benchmark_register(). See the document 124 * of that function for the detailed behavior. 125 * 126 * The gnuplot file will be named bench_test_run.gnuplot. Just run: 127 * 128 * @code 129 * gnuplot bench_test_run.gnuplot 130 * @endcode 131 * 132 * To create the graphic of the comparison curves. The image file is 133 * named output_test_run.png. 134 * 135 * @section tutorial_benchmark_advanced_usage More Advanced Usage 136 * 137 * In this section, several test will be created and run. The idea is 138 * exactly the same than in the previous section, but with some basic 139 * automatic way to run all the benchmarks. The following code 140 * benchmarks some Eina converts functions, and some Eina containers 141 * types: 142 * 143 * @code 144 * #include <stdlib.h> 145 * #include <stdio.h> 146 * #include <time.h> 147 * 148 * #include <Eina.h> 149 * 150 * static void bench_convert(Eina_Benchmark *bench); 151 * static void bench_container(Eina_Benchmark *bench); 152 * 153 * typedef struct _Benchmark_Case Benchmark_Case; 154 * 155 * struct _Benchmark_Case 156 * { 157 * const char *bench_case; 158 * void (*build)(Eina_Benchmark *bench); 159 * }; 160 * 161 * static const Benchmark_Case benchmarks[] = { 162 * { "Bench 1", bench_convert }, 163 * { "Bench 2", bench_container }, 164 * { NULL, NULL } 165 * }; 166 * 167 * static 168 * void convert1(int request) 169 * { 170 * char tmp[128]; 171 * int i; 172 * 173 * srand(time(NULL)); 174 * 175 * for (i = 0; i < request; ++i) 176 * eina_convert_itoa(rand(), tmp); 177 * } 178 * 179 * static 180 * void convert2(int request) 181 * { 182 * char tmp[128]; 183 * int i; 184 * 185 * srand(time(NULL)); 186 * 187 * for (i = 0; i < request; ++i) 188 * eina_convert_xtoa(rand(), tmp); 189 * } 190 * 191 * static void 192 * bench_convert(Eina_Benchmark *bench) 193 * { 194 * eina_benchmark_register(bench, "convert-1", EINA_BENCHMARK(convert1), 200, 400, 10); 195 * eina_benchmark_register(bench, "convert-2", EINA_BENCHMARK(convert2), 200, 400, 10); 196 * } 197 * 198 * static 199 * void array(int request) 200 * { 201 * Eina_Array *array; 202 * Eina_Array_Iterator it; 203 * int *data; 204 * int i; 205 * 206 * srand(time(NULL)); 207 * 208 * array = eina_array_new(64); 209 * 210 * for (i = 0; i < request; ++i) 211 * { 212 * data = (int *)malloc(sizeof(int)); 213 * if (!data) continue; 214 * *data = rand(); 215 * eina_array_push(array, data); 216 * } 217 * 218 * EINA_ARRAY_ITER_NEXT(array, i, data, it) 219 * free(data); 220 * 221 * eina_array_free(array); 222 * } 223 * 224 * static 225 * void list(int request) 226 * { 227 * Eina_List *l = NULL; 228 * int *data; 229 * int i; 230 * 231 * srand(time(NULL)); 232 * 233 * for (i = 0; i < request; ++i) 234 * { 235 * data = (int *)malloc(sizeof(int)); 236 * if (!data) continue; 237 * *data = rand(); 238 * l = eina_list_prepend(l, data); 239 * } 240 * 241 * while (l) 242 * { 243 * free(eina_list_data_get(l)); 244 * l = eina_list_remove_list(l, l); 245 * } 246 * } 247 * 248 * static void 249 * bench_container(Eina_Benchmark *bench) 250 * { 251 * eina_benchmark_register(bench, "array", EINA_BENCHMARK(array), 200, 300, 10); 252 * eina_benchmark_register(bench, "list", EINA_BENCHMARK(list), 200, 300, 10); 253 * } 254 * 255 * int main() 256 * { 257 * Eina_Benchmark *test; 258 * Eina_Array *ea; 259 * unsigned int i; 260 * 261 * if (!eina_init()) 262 * return EXIT_FAILURE; 263 * 264 * for (i = 0; benchmarks[i].bench_case != NULL; ++i) 265 * { 266 * test = eina_benchmark_new(benchmarks[i].bench_case, "Benchmark example"); 267 * if (!test) 268 * continue; 269 * 270 * benchmarks[i].build(test); 271 * 272 * ea = eina_benchmark_run(test); 273 * 274 * eina_benchmark_free(test); 275 * } 276 * 277 * eina_shutdown(); 278 * 279 * return EXIT_SUCCESS; 280 * } 281 * @endcode 282 * 283 * gnuplot can be used to see how are performed the convert functions 284 * together, as well as how are performed the containers. So it is now 285 * easy to see that the hexadecimal convert function is faster than 286 * the decimal one, and that arrays are faster than lists. 287 * 288 * You can improve all that by executing automatically gnuplot in your 289 * program, or integrate the Eina benchmark framework in an autotooled 290 * project. See that 291 * <a href="http://trac.enlightenment.org/e/wiki/AutotoolsIntegration#Benchmark">page</a> 292 * for more information. 293 */ 294 295 296 /** 297 * @addtogroup Eina_Benchmark_Group Benchmark 298 * 299 * These functions allow you to add a benchmark framework to a project 300 * for timing critical parts and detecting slow parts of code. It is used 301 * in Eina to compare the time used by eina, glib, evas and ecore data 302 * types. 303 * 304 * To use the benchmark module, Eina must be initialized with 305 * eina_init() and later shut down with eina_shutdown(). A benchmark 306 * is created with eina_benchmark_new() and freed with 307 * eina_benchmark_free(). 308 * 309 * eina_benchmark_register() adds a test to a benchmark. That test can 310 * be run a certain amount of times. Adding more than one test to be 311 * executed allows the comparison between several parts of a program, 312 * or different implementations. 313 * 314 * eina_benchmark_run() runs all the tests registered with 315 * eina_benchmark_register(). The amount of time of each test is 316 * written in a gnuplot file. 317 * 318 * For more information, you can look at the @ref tutorial_benchmark_page. 319 */ 320 321 /** 322 * @addtogroup Eina_Tools_Group Tools 323 * 324 * @{ 325 */ 326 327 /** 328 * @defgroup Eina_Benchmark_Group Benchmark 329 * 330 * @{ 331 */ 332 333 /** 334 * @typedef Eina_Benchmark 335 * Type for a benchmark. 336 */ 337 typedef struct _Eina_Benchmark Eina_Benchmark; 338 339 /** 340 * @typedef Eina_Benchmark_Specimens 341 * Type for a test function to be called when running a benchmark. 342 */ 343 typedef void (*Eina_Benchmark_Specimens)(int request); 344 345 /** 346 * @def EINA_BENCHMARK 347 * @brief Definition for the cast to an #Eina_Benchmark_Specimens. 348 * 349 * @param[in] function The function to cast. 350 * 351 * This macro casts @p function to Eina_Benchmark_Specimens. 352 */ 353 #define EINA_BENCHMARK(function) ((Eina_Benchmark_Specimens)function) 354 355 356 /** 357 * @brief Creates a new array. 358 * 359 * @param[in] name The name of the benchmark. 360 * @param[in] run The name of the run. 361 * @return A valid benchmark on success, or @c NULL on memory allocation 362 * failure. 363 * 364 * This function creates a new benchmark. @p name and @p run are used 365 * to name the gnuplot file that eina_benchmark_run() will create. 366 * 367 * When the new module is not needed anymore, use 368 * eina_benchmark_free() to free the allocated memory. 369 */ 370 EAPI Eina_Benchmark *eina_benchmark_new(const char *name, 371 const char *run); 372 373 /** 374 * @brief Frees a benchmark object. 375 * 376 * @param[in,out] bench The benchmark to free. 377 * 378 * This function removes all the benchmark tests that have been 379 * registered and frees @p bench. If @p bench is @c NULL, this 380 * function returns immediately. 381 */ 382 EAPI void eina_benchmark_free(Eina_Benchmark *bench); 383 384 /** 385 * @brief Adds a test to a benchmark. 386 * 387 * @param[in,out] bench The benchmark. 388 * @param[in] name The name of the test. 389 * @param[in] bench_cb The test function to be called. 390 * @param[in] count_start The start data to be passed to @p bench_cb. 391 * @param[in] count_end The end data to be passed to @p bench_cb. 392 * @param[in] count_step The step data to be passed to @p bench_cb. 393 * @return #EINA_FALSE on failure, #EINA_TRUE otherwise. 394 * 395 * This function adds the test named @p name to @p benchmark. @p 396 * bench_cb is the function called when the test is executed. That 397 * test can be executed a certain number of times, from @p count_start 398 * to @p count_end, with a step increment of @p count_step. This counter 399 * is passed to @p bench_cb when eina_benchmark_run() is called. 400 * 401 * If @p bench is @c NULL or @p count_step is 0, this function returns 402 * immediately and does not add any tests to the benchmark. 403 */ 404 EAPI Eina_Bool eina_benchmark_register(Eina_Benchmark *bench, 405 const char *name, 406 Eina_Benchmark_Specimens bench_cb, 407 int count_start, 408 int count_end, 409 int count_step); 410 411 /** 412 * @brief Runs the benchmark's registered tests. 413 * 414 * @param[in,out] bench The benchmark. 415 * @return A list of gnuplot filenames for the test results, or @c NULL 416 * on failure. 417 * 418 * This function runs all the tests that have been registered with 419 * eina_benchmark_register() and saves the result in gnuplot 420 * input files. The filenames have the following format: 421 * 422 * @code 423 * bench_[name]_[run]%s.gnuplot 424 * @endcode 425 * 426 * Where [name] and [run] are the values passed to 427 * eina_benchmark_new() when registering the test. 428 * 429 * Each registered test is executed and timed. The time is written to 430 * the gnuplot file. The number of times each test is executed is 431 * controlled by the parameters passed to eina_benchmark_register(). 432 */ 433 EAPI Eina_Array *eina_benchmark_run(Eina_Benchmark *bench); 434 435 /** 436 * @} 437 */ 438 439 /** 440 * @} 441 */ 442 443 #endif /* EINA_BENCHMARK_H_ */ 444