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