Re: [PATCH] libuuid: use kernel crypto api

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, 4 Aug 2018, Theodore Y. Ts'o wrote:

> On Sat, Aug 04, 2018 at 08:17:06PM +0100, Sami Kerola wrote:
> > Try to speed up md5 checksums by using hardware accelerators when they are
> > present with use of kernel crypto api.
> > 
> > Reference: http://www.chronox.de/libkcapi/html/ch01s02.html
> > Signed-off-by: Sami Kerola <kerolasa@xxxxxx>
> 
> I'm not sure I see the point of this change.  Is there a use case you
> have in mind where people would be using crypto hash based UUID's
> where performance is a concern?  In in that case where someone was
> trying to create a huge number of crypto hash based UUID's, how much
> overhead is there in going through the kernel API if there is *not* a
> hardware accelerator present?
> 
> 						- Ted

Hi Dmitry & Ted,

I should have told in that commit message part of the motivation was to 
deprecate util-linux local md5 implementation. But since both of you 
raised concern about performance I decided to test kernel api and 
util-linux implementations as close the same way as they are used in 
libuuid.

Executive summary: kernel api is surprisingly slow.

The following test is using short input messages (1 to 64 bytes) to md5 
functions. I assume most uuid use is in this range. As you can see in perf 
print out the kernel api is about 10x slower. When inputs are up to 1024 
bytes slowdown is about x4. With 10 kilobyte chucks these are about as 
quick. Maybe I am wrong when thinking most uuid's tend to be based on 
small inputs, so the slow down is relevant.

Thank you both for pointing out the change smelled rats. I did not realize 
kernel api could be so expensive to use. That said I am no longer in 
favour of merging this change.

/*
$ dd if=/dev/urandom of=./test.in count=1 bs=10M
$ cat ./test.in >/dev/null # ensure page cache is warm

$ perf stat md5-test --kernel-api ./test.in

 Performance counter stats for 'md5-test --kernel-api ./test.in':

       2462.555389      task-clock:u (msec)       #    0.963 CPUs utilized
                 0      context-switches:u        #    0.000 K/sec
                 0      cpu-migrations:u          #    0.000 K/sec
                56      page-faults:u             #    0.023 K/sec
       213,119,356      cycles:u                  #    0.087 GHz
        65,763,609      instructions:u            #    0.31  insn per cycle
        19,412,627      branches:u                #    7.883 M/sec
           131,530      branch-misses:u           #    0.68% of all branches

       2.557148339 seconds time elapsed

$ perf stat md5-test --util-linux ./test.in

 Performance counter stats for 'md5-test --util-linux ./test.in':

        225.212947      task-clock:u (msec)       #    0.998 CPUs utilized
                 0      context-switches:u        #    0.000 K/sec
                 0      cpu-migrations:u          #    0.000 K/sec
                57      page-faults:u             #    0.253 K/sec
       225,921,565      cycles:u                  #    1.003 GHz
       375,010,554      instructions:u            #    1.66  insn per cycle
        21,918,050      branches:u                #   97.321 M/sec
            48,356      branch-misses:u           #    0.22% of all branches

       0.225656336 seconds time elapsed
*/

#include <getopt.h>
#include <linux/if_alg.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

#include "c.h"
#include "closestream.h"
#include "nls.h"
#include "strutils.h"
#include "uuid.h"
#include "xalloc.h"
#include "md5.h"

struct run_state {
	unsigned int api;
	unsigned int seed;
	size_t max_read;
	int fd;
};

static void __attribute__((__noreturn__)) usage(void)
{
	fputs(USAGE_HEADER, stdout);
	printf(" %s [options] file\n", program_invocation_short_name);

	fputs(USAGE_SEPARATOR, stdout);
	puts("Compare util-linux md5 with kernel crypto api.");

	fputs(USAGE_OPTIONS, stdout);
	puts(" -u, --util-linux       use util-linux md5 implementation (default)");
	puts(" -k, --kernel-api       use kernel api");
	puts(" -s, --seed <number>    srand(seed) value (default: 42)");
	puts(" -m, --max <number>     maximum read chunk from file (default: 64)");
	fputs(USAGE_SEPARATOR, stdout);
	printf(USAGE_HELP_OPTIONS(24));
	printf(USAGE_MAN_TAIL("md5-test(1)"));
	exit(EXIT_SUCCESS);
}

static void kernel_api(uuid_t out, const uuid_t ns, char *buf, size_t sz)
{
	int tfmfd;
	struct sockaddr_alg sa = {
		.salg_family = AF_ALG,
		.salg_type = "hash",
		.salg_name = "md5"
	};
	int opfd = -1, fail = 0;
	struct iovec const iov[2] = {
		{.iov_base = (void *)ns, .iov_len = sizeof(uuid_t)},
		{.iov_base = (void *)buf, .iov_len = sz}
	};

	tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
	if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
		fail = 1;
	if (!fail && (opfd = accept(tfmfd, NULL, 0)) < 0)
		fail = 1;
	if (!fail && writev(opfd, iov, 2) < 0)
		fail = 1;
	if (!fail && read(opfd, out, sizeof(uuid_t)) == -1)
		fail = 1;
	if (opfd != -1)
		close(opfd);
	close(tfmfd);
}

static void util_linux_md5(uuid_t out, const uuid_t ns, char *buf, size_t sz)
{
	UL_MD5_CTX ctx;
	char hash[UL_MD5LENGTH];

	ul_MD5Init(&ctx);
	/* hash concatenation of well-known UUID with name */
	ul_MD5Update(&ctx, ns, sizeof(uuid_t));
	ul_MD5Update(&ctx, (const unsigned char *)buf, sz);

	ul_MD5Final((unsigned char *)hash, &ctx);

	memcpy(out, hash, sizeof(uuid_t));
}

int main(int argc, char **argv)
{
	struct run_state rst = {
		.api = 0,
		.seed = 42,
		.max_read = 64,
		.fd = -1
	};
	static const struct option longopts[] = {
		{ "util-linux", no_argument,       NULL, 'u' },
		{ "kernel-api", no_argument,       NULL, 'k' },
		{ "seed",       required_argument, NULL, 's' },
		{ "max",        required_argument, NULL, 'm' },
		{ "version",    no_argument,       NULL, 'V' },
		{ "help",       no_argument,       NULL, 'h' },
		{ NULL, 0, NULL, 0 }
	};
	int c;
	char *buf;
	uuid_t out;
	uuid_t ns;

	atexit(close_stdout);

	while ((c = getopt_long(argc, argv, "uks:m:Vh", longopts, NULL)) != -1)
		switch (c) {
		case 'u':
			rst.api = 0;
			break;
		case 'k':
			rst.api = 1;
			break;
		case 's':
			rst.seed = strtou32_or_err(optarg, "failed to parse seed");
			break;
		case 'm':
			rst.max_read = strtou32_or_err(optarg, "failed to parse max chunk");
			if (rst.max_read == 0)
				err(EXIT_FAILURE, "max cannot be zero");
			break;
		case 'V':
			printf(UTIL_LINUX_VERSION);
			return EXIT_SUCCESS;
		case 'h':
			usage();
		default:
			errtryhelp(EXIT_FAILURE);
		}

	argc -= optind;
	argv += optind;
	if (0 < argc) {
		rst.fd = open(argv[0], O_RDONLY);
		if (rst.fd < 0)
			err(EXIT_FAILURE, "could not open: %s", argv[0]);
	} else
		errx(EXIT_FAILURE, "mandatory file argument missing");

	buf = xmalloc(rst.max_read);
	srand(rst.seed);

	while(1) {
		ssize_t sz;

		sz = (rand() % rst.max_read) + 1;
		if (read(rst.fd, buf, sz) != sz)
			break;
		if (rst.api)
			kernel_api(out, ns, buf, sz);
		else
			util_linux_md5(out, ns, buf, sz);
	}
	free(buf);
	close(rst.fd);
	return EXIT_SUCCESS;
}
--
To unsubscribe from this list: send the line "unsubscribe util-linux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux