1 /*
2 * This software is Copyright (c) 2018 magnum
3 * and is hereby released to the general public under the following terms:
4 * Redistribution and use in source and binary forms, with or without
5 * modifications, are permitted.
6 */
7
8 #ifdef _OPENMP
9 #include <omp.h>
10 #endif
11
12 #include "john.h"
13 #include "options.h"
14 #include "logger.h"
15 #include "formats.h"
16 #include "timer.h"
17 #include "config.h"
18
19 #define CONF_SECTION SECTION_OPTIONS, ":CPUtune"
20
21 static int use_preset, max_no_progress;
22 static double sample_time, req_gain, max_tune_time;
23
24 static struct fmt_main *fmt;
25 static int omp_autotune_running;
26 static int mkpc;
27 static int omp_scale;
28 static int report;
29 static int fmt_preset;
30 static int tune_preset;
31 static int scale = 1;
32
omp_autotune_init(void)33 void omp_autotune_init(void)
34 {
35 int ci;
36
37 #if __i386__ || __x86_64__ || __MIC__
38 use_preset = cfg_get_bool(CONF_SECTION, "UsePreset", 1);
39 #else
40 // Our presets are from intel[tm] CPUs. Anything else should autotune
41 use_preset = 0;
42 #endif
43 if ((ci = cfg_get_int(CONF_SECTION, "AutoTuneSampleTime")) < 0)
44 ci = 10;
45 sample_time = (double)ci / 1000.0;
46 if ((ci = cfg_get_int(CONF_SECTION, "AutoTuneReqGain")) < 0)
47 ci = 5;
48 req_gain = (double)ci / 100.0 + 1.0;
49 if ((ci = cfg_get_int(CONF_SECTION, "AutoTuneMaxDuration")) < 0)
50 ci = 100;
51 max_tune_time = (double)ci / 1000.0;
52 if ((max_no_progress =
53 cfg_get_int(CONF_SECTION, "AutoTuneMaxNoProgress")) < 0)
54 max_no_progress = 3;
55
56 if (options.tune) {
57 if (!strcmp(options.tune, "auto"))
58 use_preset = 0;
59 else if (!strcmp(options.tune, "report")) {
60 use_preset = 0;
61 report = 1;
62 } else {
63 use_preset = 1;
64 tune_preset = atoi(options.tune);
65 }
66 }
67 }
68
omp_autotune(struct fmt_main * format,int preset)69 int omp_autotune(struct fmt_main *format, int preset)
70 {
71 #ifdef _OPENMP
72 int threads = (format->params.flags & FMT_OMP) ? omp_get_max_threads() : 1;
73 #else
74 int threads = 1;
75 #endif
76 int ret_scale;
77
78 fmt_preset = preset;
79
80 omp_scale = tune_preset ? tune_preset : (use_preset ? preset : 0);
81 ret_scale = tune_preset ? tune_preset : (threads == 1 ? 1 : (omp_scale ? omp_scale : 1));
82
83 if (omp_autotune_running)
84 return threads * scale;
85
86 if (!use_preset || !preset) {
87 fmt = format;
88 mkpc = format->params.max_keys_per_crypt;
89 }
90 format->params.min_keys_per_crypt *= threads;
91 format->params.max_keys_per_crypt *= threads * ret_scale;
92
93 return threads * ret_scale;
94 }
95
omp_autotune_run(struct db_main * db)96 void omp_autotune_run(struct db_main *db)
97 {
98 #ifdef _OPENMP
99 int threads =
100 fmt ? ((fmt->params.flags & FMT_OMP) ? omp_get_max_threads() : 1) : 1;
101 #else
102 int threads = 1;
103 #endif
104 int best_scale = 1;
105 int best_cps = 0;
106 int no_progress = 0;
107 int min_crypts = 0;
108 int tune_cost;
109 void *salt;
110 char key[PLAINTEXT_BUFFER_SIZE] = "tUne0000";
111 sTimer timer;
112 double duration;
113
114 if (!fmt || omp_scale == 1 || tune_preset)
115 goto cleanup;
116
117 if (john_main_process && (options.flags & FLG_TEST_CHK) &&
118 ((options.tune && !strcmp(options.tune, "report")) ||
119 options.verbosity > VERB_DEFAULT))
120 printf("\n");
121
122 scale = 1;
123 omp_autotune_running = 1;
124
125 // Find most expensive salt, for auto-tune
126 {
127 struct db_salt *s = db->salts;
128
129 tune_cost = MIN(db->max_cost[0], options.loader.max_cost[0]);
130
131 while (s->next && s->cost[0] < tune_cost)
132 s = s->next;
133 salt = s->salt;
134 }
135
136 if (john_main_process && options.verbosity >= VERB_MAX) {
137 printf("%s %s autotune using %s db",
138 fmt->params.label, threads > 1 ? "OMP" : "MKPC",
139 db->real ? "real" : "test");
140 if (fmt->methods.tunable_cost_value[0])
141 printf(" with %s of %d\n",
142 fmt->params.tunable_cost_name[0], tune_cost);
143 printf("\n");
144 }
145
146 sTimer_Init(&timer);
147 do {
148 int i;
149 int min_kpc = fmt->params.min_keys_per_crypt;
150 int this_kpc = mkpc * threads * scale;
151 int cps, crypts = 0;
152
153 if (threads == 1)
154 this_kpc = min_kpc * scale; // We're tuning MKPC
155
156 fmt->params.max_keys_per_crypt = this_kpc;
157
158 // Release old buffers
159 fmt->methods.done();
160
161 // Set up buffers for this test
162 fmt->methods.init(fmt);
163
164 // Format may have bumped kpc in init()
165 this_kpc = fmt->params.max_keys_per_crypt;
166
167 // Load keys
168 fmt->methods.clear_keys();
169 for (i = 0; i < this_kpc; i++) {
170 key[4] = '0' + (i / 1000) % 10;
171 key[5] = '0' + (i / 100) % 10;
172 key[6] = '0' + (i / 10) % 10;
173 key[7] = '0' + i % 10;
174 fmt->methods.set_key(key, i);
175 }
176
177 // Set the salt we picked earlier
178 fmt->methods.set_salt(salt);
179
180 // Tell format this is a speed test
181 benchmark_running++;
182
183 sTimer_Start(&timer, 1);
184 do {
185 int count = this_kpc;
186
187 fmt->methods.crypt_all(&count, NULL);
188 crypts += count;
189 } while (crypts < min_crypts || sTimer_GetSecs(&timer) < sample_time);
190 sTimer_Stop(&timer);
191
192 benchmark_running--;
193
194 duration = sTimer_GetSecs(&timer);
195 cps = crypts / duration;
196
197 if (john_main_process && options.verbosity >= VERB_MAX) {
198 if (threads > 1)
199 printf("OMP scale %d: %d crypts (%dx%d) in %f seconds, %d c/s",
200 scale, crypts, crypts / this_kpc, this_kpc, duration, cps);
201 else
202 printf("MKPC %d: %d crypts (%dx%d) in %f seconds, %d c/s",
203 this_kpc, crypts, crypts / this_kpc, this_kpc,
204 duration, cps);
205 }
206
207 if (cps >= (best_cps * req_gain)) {
208 if (john_main_process && options.verbosity >= VERB_MAX)
209 printf(" +\n");
210 best_cps = cps;
211 best_scale = scale;
212 no_progress = 0;
213 }
214 else {
215 if (john_main_process && options.verbosity >= VERB_MAX)
216 printf("\n");
217 no_progress++;
218 }
219
220 min_crypts = crypts;
221
222 if (duration > max_tune_time || no_progress >= max_no_progress)
223 break;
224
225 if (threads == 1 && min_kpc == 1) {
226 int quick_move = 1;
227
228 while (crypts / this_kpc / quick_move > 8192) {
229 quick_move *= 2;
230 scale *= 2;
231 }
232 }
233
234 // Double each time
235 scale *= 2;
236 } while (1);
237
238 if (options.tune && !strcmp(options.tune, "report")) {
239 if (threads == 1) {
240 if (best_scale * fmt->params.min_keys_per_crypt != mkpc)
241 printf("Autotuned MKPC %d, preset is %d\n",
242 best_scale * fmt->params.min_keys_per_crypt, mkpc);
243 } else {
244 if (best_scale != fmt_preset)
245 printf("Autotuned OMP scale %d, preset is %d\n",
246 best_scale, fmt_preset);
247 }
248 } else {
249 if (threads == 1) {
250 if (john_main_process && options.verbosity > VERB_DEFAULT)
251 printf("Autotune found best speed at MKPC of %d (%d * %d)\n",
252 best_scale * fmt->params.min_keys_per_crypt,
253 best_scale, fmt->params.min_keys_per_crypt);
254 log_event("Autotune found best speed at MKPC of %d (%d * %d)",
255 best_scale * fmt->params.min_keys_per_crypt,
256 best_scale, fmt->params.min_keys_per_crypt);
257 } else {
258 if (john_main_process && options.verbosity > VERB_DEFAULT)
259 printf("Autotune found best speed at OMP scale of %d\n",
260 best_scale);
261 log_event("Autotune found best speed at OMP scale of %d", best_scale);
262 }
263 }
264
265 if (best_scale != scale) {
266 scale = best_scale;
267
268 // Release old buffers
269 fmt->methods.done();
270
271 // Set up buffers for chosen scale
272 fmt->methods.init(fmt);
273 }
274
275 cleanup:
276 omp_autotune_running = 0;
277 fmt = NULL;
278 omp_scale = 0;
279 mkpc = 0;
280 scale = 1;
281
282 return;
283 }
284