1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "../fio.h"
6#include "../gettime.h"
7#include "../fio_time.h"
8#include "../verify.h"
9
10#include "../crc/md5.h"
11#include "../crc/crc64.h"
12#include "../crc/crc32.h"
13#include "../crc/crc32c.h"
14#include "../crc/crc16.h"
15#include "../crc/crc7.h"
16#include "../crc/sha1.h"
17#include "../crc/sha256.h"
18#include "../crc/sha512.h"
19#include "../crc/xxhash.h"
20#include "../crc/murmur3.h"
21#include "../crc/fnv.h"
22#include "../hash.h"
23
24#include "test.h"
25
26#define CHUNK		131072U
27#define NR_CHUNKS	  2048U
28
29struct test_type {
30	const char *name;
31	unsigned int mask;
32	void (*fn)(struct test_type *, void *, size_t);
33	uint32_t output;
34};
35
36enum {
37	T_MD5		= 1U << 0,
38	T_CRC64		= 1U << 1,
39	T_CRC32		= 1U << 2,
40	T_CRC32C	= 1U << 3,
41	T_CRC16		= 1U << 4,
42	T_CRC7		= 1U << 5,
43	T_SHA1		= 1U << 6,
44	T_SHA256	= 1U << 7,
45	T_SHA512	= 1U << 8,
46	T_XXHASH	= 1U << 9,
47	T_MURMUR3	= 1U << 10,
48	T_JHASH		= 1U << 11,
49	T_FNV		= 1U << 12,
50};
51
52static void t_md5(struct test_type *t, void *buf, size_t size)
53{
54	uint32_t digest[4];
55	struct fio_md5_ctx ctx = { .hash = digest };
56	int i;
57
58	fio_md5_init(&ctx);
59
60	for (i = 0; i < NR_CHUNKS; i++) {
61		fio_md5_update(&ctx, buf, size);
62		fio_md5_final(&ctx);
63	}
64}
65
66static void t_crc64(struct test_type *t, void *buf, size_t size)
67{
68	int i;
69
70	for (i = 0; i < NR_CHUNKS; i++)
71		fio_crc64(buf, size);
72}
73
74static void t_crc32(struct test_type *t, void *buf, size_t size)
75{
76	int i;
77
78	for (i = 0; i < NR_CHUNKS; i++)
79		fio_crc32(buf, size);
80}
81
82static void t_crc32c(struct test_type *t, void *buf, size_t size)
83{
84	int i;
85
86	for (i = 0; i < NR_CHUNKS; i++)
87		fio_crc32c(buf, size);
88}
89
90static void t_crc16(struct test_type *t, void *buf, size_t size)
91{
92	int i;
93
94	for (i = 0; i < NR_CHUNKS; i++)
95		fio_crc16(buf, size);
96}
97
98static void t_crc7(struct test_type *t, void *buf, size_t size)
99{
100	int i;
101
102	for (i = 0; i < NR_CHUNKS; i++)
103		fio_crc7(buf, size);
104}
105
106static void t_sha1(struct test_type *t, void *buf, size_t size)
107{
108	uint32_t sha[5];
109	struct fio_sha1_ctx ctx = { .H = sha };
110	int i;
111
112	fio_sha1_init(&ctx);
113
114	for (i = 0; i < NR_CHUNKS; i++) {
115		fio_sha1_update(&ctx, buf, size);
116		fio_sha1_final(&ctx);
117	}
118}
119
120static void t_sha256(struct test_type *t, void *buf, size_t size)
121{
122	uint8_t sha[64];
123	struct fio_sha256_ctx ctx = { .buf = sha };
124	int i;
125
126	fio_sha256_init(&ctx);
127
128	for (i = 0; i < NR_CHUNKS; i++) {
129		fio_sha256_update(&ctx, buf, size);
130		fio_sha256_final(&ctx);
131	}
132}
133
134static void t_sha512(struct test_type *t, void *buf, size_t size)
135{
136	uint8_t sha[128];
137	struct fio_sha512_ctx ctx = { .buf = sha };
138	int i;
139
140	fio_sha512_init(&ctx);
141
142	for (i = 0; i < NR_CHUNKS; i++)
143		fio_sha512_update(&ctx, buf, size);
144}
145
146static void t_murmur3(struct test_type *t, void *buf, size_t size)
147{
148	int i;
149
150	for (i = 0; i < NR_CHUNKS; i++)
151		murmurhash3(buf, size, 0x8989);
152}
153
154static void t_jhash(struct test_type *t, void *buf, size_t size)
155{
156	int i;
157
158	for (i = 0; i < NR_CHUNKS; i++)
159		t->output += jhash(buf, size, 0x8989);
160}
161
162static void t_fnv(struct test_type *t, void *buf, size_t size)
163{
164	int i;
165
166	for (i = 0; i < NR_CHUNKS; i++)
167		t->output += fnv(buf, size, 0x8989);
168}
169
170static void t_xxhash(struct test_type *t, void *buf, size_t size)
171{
172	void *state;
173	int i;
174
175	state = XXH32_init(0x8989);
176
177	for (i = 0; i < NR_CHUNKS; i++)
178		XXH32_update(state, buf, size);
179
180	t->output = XXH32_digest(state);
181}
182
183static struct test_type t[] = {
184	{
185		.name = "md5",
186		.mask = T_MD5,
187		.fn = t_md5,
188	},
189	{
190		.name = "crc64",
191		.mask = T_CRC64,
192		.fn = t_crc64,
193	},
194	{
195		.name = "crc32",
196		.mask = T_CRC32,
197		.fn = t_crc32,
198	},
199	{
200		.name = "crc32c",
201		.mask = T_CRC32C,
202		.fn = t_crc32c,
203	},
204	{
205		.name = "crc16",
206		.mask = T_CRC16,
207		.fn = t_crc16,
208	},
209	{
210		.name = "crc7",
211		.mask = T_CRC7,
212		.fn = t_crc7,
213	},
214	{
215		.name = "sha1",
216		.mask = T_SHA1,
217		.fn = t_sha1,
218	},
219	{
220		.name = "sha256",
221		.mask = T_SHA256,
222		.fn = t_sha256,
223	},
224	{
225		.name = "sha512",
226		.mask = T_SHA512,
227		.fn = t_sha512,
228	},
229	{
230		.name = "xxhash",
231		.mask = T_XXHASH,
232		.fn = t_xxhash,
233	},
234	{
235		.name = "murmur3",
236		.mask = T_MURMUR3,
237		.fn = t_murmur3,
238	},
239	{
240		.name = "jhash",
241		.mask = T_JHASH,
242		.fn = t_jhash,
243	},
244	{
245		.name = "fnv",
246		.mask = T_FNV,
247		.fn = t_fnv,
248	},
249	{
250		.name = NULL,
251	},
252};
253
254static unsigned int get_test_mask(const char *type)
255{
256	char *ostr, *str = strdup(type);
257	unsigned int mask;
258	char *name;
259	int i;
260
261	ostr = str;
262	mask = 0;
263	while ((name = strsep(&str, ",")) != NULL) {
264		for (i = 0; t[i].name; i++) {
265			if (!strcmp(t[i].name, name)) {
266				mask |= t[i].mask;
267				break;
268			}
269		}
270	}
271
272	free(ostr);
273	return mask;
274}
275
276static int list_types(void)
277{
278	int i;
279
280	for (i = 0; t[i].name; i++)
281		printf("%s\n", t[i].name);
282
283	return 1;
284}
285
286int fio_crctest(const char *type)
287{
288	unsigned int test_mask = 0;
289	uint64_t mb = CHUNK * NR_CHUNKS;
290	struct frand_state state;
291	int i, first = 1;
292	void *buf;
293
294	crc32c_intel_probe();
295
296	if (!type)
297		test_mask = ~0U;
298	else if (!strcmp(type, "help") || !strcmp(type, "list"))
299		return list_types();
300	else
301		test_mask = get_test_mask(type);
302
303	if (!test_mask) {
304		fprintf(stderr, "fio: unknown hash `%s`. Available:\n", type);
305		return list_types();
306	}
307
308	buf = malloc(CHUNK);
309	init_rand_seed(&state, 0x8989);
310	fill_random_buf(&state, buf, CHUNK);
311
312	for (i = 0; t[i].name; i++) {
313		struct timeval tv;
314		double mb_sec;
315		uint64_t usec;
316		char pre[3];
317
318		if (!(t[i].mask & test_mask))
319			continue;
320
321		/*
322		 * For first run, make sure CPUs are spun up and that
323		 * we've touched the data.
324		 */
325		if (first) {
326			usec_spin(100000);
327			t[i].fn(&t[i], buf, CHUNK);
328		}
329
330		fio_gettime(&tv, NULL);
331		t[i].fn(&t[i], buf, CHUNK);
332		usec = utime_since_now(&tv);
333
334		if (usec) {
335			mb_sec = (double) mb / (double) usec;
336			mb_sec /= (1.024 * 1.024);
337			if (strlen(t[i].name) >= 7)
338				sprintf(pre, "\t");
339			else
340				sprintf(pre, "\t\t");
341			printf("%s:%s%8.2f MB/sec\n", t[i].name, pre, mb_sec);
342		} else
343			printf("%s:inf MB/sec\n", t[i].name);
344		first = 0;
345	}
346
347	free(buf);
348	return 0;
349}
350