-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathREADME
More file actions
250 lines (178 loc) · 8.74 KB
/
Copy pathREADME
File metadata and controls
250 lines (178 loc) · 8.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
_ _ __ __ _____ _____ ______ _ _
|_____| \_/ |_____ |_____] | |______ \___/
| | | _____| | |_____ |______ _/ \_
-------------------------------------------------------
What is it about?
-----------------
Hysplex is a C library that helps you measure performance of different
implementations of a function.
What is the easiest way of cloning it?
--------------------------------------
_ git clone https://github.com/rafael-santiago/hysplex hysplex --recursive
How can I build it?
-------------------
You need to use my personal build system called Hefesto
<https://github.com/rafael-santiago/hefesto>.
After following the steps to make Hefesto work on your environment,
you need to move to src sub-directory and invoke Hefesto from there.
_ cd hysplex/src
_ hefesto
If all worked you will get hysplex ar file at '../lib/libhysplex.a'.
Using hysplex
-------------
Hysplex is based on an internal DSL. The following code will able to
measure the performance of two different implementations of a simple
integer sum function:
#include <hysplex.h>
#include <stdio.h>
#include <unistd.h>
static int plain_sum(const int a, const int b) {
return a + b;
}
static int delayed_sum(const int a, const int b) {
usleep(1);
return a + b;
}
HYSPLEX_EVAL_PAIR(100000, 1, {}, {}, plain_sum, delayed_sum, 1, 2);
The only DSL statement that is necessary to do the task is HYSPLEX_EVAL_PAIR.
HYSPLEX_EVAL_PAIR allows you measure performance of two functions. It expects:
- A default number of iterations (how many times those functions will run).
- If you want or not warm up (run functions without any benchmarking just
to warm up cache).
- A set of pre statements to execute before calling the functions.
- A set of post statements to execute after calling the functions.
- The two functions.
- The parameter list that must be passed to those functions.
In order to compile you need to include -I<directory path to hysplex.h>,
-L<directory path to libhysplex.a> -lhysplex. E.g.:
_ gcc -I~/hysplex/src -L~/hysplex/lib eval_pair.c -oeval-pair -lhysplex
After compiling the source you will get an application suitable to
measure your functions and present to you the results of all benchmarking.
All you should do is:
_ ./eval-pair
You will get a thing similar to this:
== Hysplex final stats
== Functions 'plain_sum' and 'delayed_sum' were executed 100000 time(s).
== Function 'plain_sum' has won 99963 time(s).
== Function 'delayed_sum' has won 144 time(s).
== Total of tied executions: 53.
== Total of iterations with a winner: 99947
== The average execution time of function 'plain_sum' was about 0.000000 secs.
== The average execution time of function 'delayed_sum' was about 0.000011 secs.
== The winner function is 'plain_sum'.
== Chi-square = 99692.192 (certainty = 95%), 'plain_sum' is statistically faster than 'delayed_sum'.
As you can see, Hysplex figured out that 'plain_sum' is really faster than 'delayed_sum' (of course).
The nice thing here is that a Chi-square test was executed against the benchmarking data in order to
ensure relevance or not between the found performance difference.
You can configure at run-time Chi-square's certainty by passing to your benchmarking executable the
option '--certainty-perc=<percentual>'. The percentual values can be: 90, 95 (default), 97, 99 and 100.
You can also overwrite the total of iterations passing the option '--iterations=<value>'.
If you want to divert the results to a file use the option '--out=<file path>'. If the provided file path provided
already exists the result data will be appended to this file.
Sometimes you will implement a bunch of different implementations of the same function. Test them paired
can be tedious when using HYSPLEX_EVAIL_PAIR besides error prone (repetitive tasks delegated to Humans
lead to errors, avoid doing this, better using your brain to think...)
To test a group of functions you should use HYSPLEX_EVAL_GROUP. When using eval group DSL statement
you need to pass a function group declared by using HYSPLEX_FUNCTION_GROUP. Take a look at the
following code:
#include <hysplex.h>
#include <sys/stat.h>
#include <fcntl.h>
int stat_based_file_exists(const char *filepath) {
if (filepath == NULL) {
return 0;
}
struct stat st;
return (stat(filepath, &st) == 0);
}
int fopen_based_file_exists(const char *filepath) {
if (filepath == NULL) {
return 0;
}
FILE *fp;
int retval = ((fp = fopen(filepath, "r")) != NULL);
if (retval) {
fclose(fp);
}
return retval;
}
int open_based_file_exists(const char *filepath) {
if (filepath == NULL) {
return 0;
}
int fd;
int retval = ((fd = open(filepath, O_RDONLY)) != -1);
if (retval > -1) {
close(fd);
}
return retval;
}
HYSPLEX_FUNCTION_GROUP_BEGIN(func_group)
HYSPLEX_REGISTER_FUNCTION(fopen_based_file_exists),
HYSPLEX_REGISTER_FUNCTION(stat_based_file_exists),
HYSPLEX_REGISTER_FUNCTION(open_based_file_exists),
HYSPLEX_FUNCTION_GROUP_END
HYSPLEX_EVAL_GROUP(100000, 1,
{
FILE *fp = fopen("test.dat", "wb");
fclose(fp);
},
{
remove("test.dat");
},
func_group, "test.dat");
HYSPLEX_EVAL_GROUP expects the same arguments expected by HYSPLEX_EVAL_PAIR,
excepting the two functions. HYSPLEX_EVAL_GROUP expects the previous declared
function group, where you have registered all functions that will run this
"marathon".
To compile:
_ gcc -I~/hysplex/src -L~/hysplex/lib eval_group.c -oeval-group -lhysplex
To run:
_ ./eval-group
== Hysplex intermediate stats
== Functions 'fopen_based_file_exists' and 'stat_based_file_exists' were executed 100000 time(s).
== Function 'fopen_based_file_exists' has won 43313 time(s).
== Function 'stat_based_file_exists' has won 99365 time(s).
== Total of tied executions: 21339.
== Total of iterations with a winner: 78661
== The average execution time of function 'fopen_based_file_exists' was about 0.000002 secs.
== The average execution time of function 'stat_based_file_exists' was about 0.000001 secs.
== The winner function is 'stat_based_file_exists'.
== Chi-square = 45731.278 (certainty = 95%), 'stat_based_file_exists' is statistically faster than 'fopen_based_file_exists'.
== Hysplex final stats
== Functions 'stat_based_file_exists' and 'open_based_file_exists' were executed 100000 time(s).
== Function 'stat_based_file_exists' has won 84572 time(s).
== Function 'open_based_file_exists' has won 61371 time(s).
== Total of tied executions: 22971.
== Total of iterations with a winner: 77029
== The average execution time of function 'stat_based_file_exists' was about 0.000001 secs.
== The average execution time of function 'open_based_file_exists' was about 0.000001 secs.
== The winner function is 'stat_based_file_exists'.
== Chi-square = 13839.709 (certainty = 95%), 'stat_based_file_exists' is statistically faster than 'open_based_file_exists'.
As you can see, use stat() to check file existence is the fastest way among the three tested ways.
Hysplex also gives you the tip when performance is a pointless subject. Let's consider the implementation of the two
following functions:
static int sum_with_copy(const int a, const int b) {
int aa = a;
int bb = b;
return aa + bb;
}
static int sum_with_xor_swap(const int a, const int b) {
int aa = a ^ b;
int bb = aa ^ b;
aa ^= bb;
return aa + bb;
}
Would xoring swap faster than a copy?
Well, now getting the answer is a straightforward task, look:
HYSPLEX_EVAL_PAIR(100000, 1, {}, {}, sum_with_copy, sum_with_xor_swap, 1, 2);
To compile:
_ gcc -I~/hysplex/src -L~/hysplex/lib eval_pair_inconclusive.c -oeval-pair-inconclusive -lhysplex
To run:
_ ./eval-pair-inconclusive
hysplex warn: The evaluated performances are inconclusive. They are almost the same in performance. A bias was detected.
The second function seems to take advantage from the first, probably due to CPU cache.
You should take into consideration other aspects than performance to pick one. It is up to you!
The result of "eval-pair-inconclusive" can vary from a computer to another. Sometimes Hysplex can tell you that the
two functions are the same in performance even not being detected any execution bias. Well, even so, it is inconclusive
anyway.