r3653 - in branches/2.0/varnish-cache: bin/varnishd include

tfheen at projects.linpro.no tfheen at projects.linpro.no
Fri Feb 6 12:47:25 CET 2009


Author: tfheen
Date: 2009-02-06 12:47:24 +0100 (Fri, 06 Feb 2009)
New Revision: 3653

Added:
   branches/2.0/varnish-cache/bin/varnishd/hash_critbit.c
Modified:
   branches/2.0/varnish-cache/bin/varnishd/Makefile.am
   branches/2.0/varnish-cache/bin/varnishd/cache_hash.c
   branches/2.0/varnish-cache/bin/varnishd/hash_slinger.h
   branches/2.0/varnish-cache/bin/varnishd/mgt_param.c
   branches/2.0/varnish-cache/bin/varnishd/varnishd.c
   branches/2.0/varnish-cache/include/stat_field.h
Log:
Merge r3454+r3455:

Add preliminary version of lock-less tree based lookup (see below)

Enable SHA256 digests by default, and put it in the objhead.  This
increases the size of the objhead by 32 bytes, but may drop
a bit again later, when other now unnecessary fields go away.

Test SHA256 for correct operation on startup.

About the "critbit" lookup:

To enable this, use "-hcritbit" argument.

"Crit Bit" trees, are also known under various other names, the original
version of the idea is probably the PATRICIA tree.

The basic concept is a tree structure which has nodes only where necessary
to tell the indices apart.

Our version of it, has some additional bells and whistles.

First lookups do not require any locks until we reach the objhead
we were looking for, or until we need to insert one which wasn't
there.

Second, the branch nodes are part of the objhead, as all but the
very first will need one, this saves malloc operations big time.

Now the down-sides:

There are still missing bits, amongst these the "cooling off" list,
for objheads that have been dereferenced, but where the branch-node
is not.  Currently we just leak that memory.

There is a race relating to node deref and unlocked lookup that is
not closed, weird things may happen until I fix it.

I'd be interested to hear how long it survives before it croaks,
but apart from that, would not advocate that you use it, until
I fix those remaining issues.



Modified: branches/2.0/varnish-cache/bin/varnishd/Makefile.am
===================================================================
--- branches/2.0/varnish-cache/bin/varnishd/Makefile.am	2009-02-06 11:38:07 UTC (rev 3652)
+++ branches/2.0/varnish-cache/bin/varnishd/Makefile.am	2009-02-06 11:47:24 UTC (rev 3653)
@@ -40,6 +40,7 @@
 	cache_vrt_re.c \
 	cache_ws.c \
 	hash_classic.c \
+	hash_critbit.c \
 	hash_simple_list.c \
 	instance.c \
 	mgt_child.c \

Modified: branches/2.0/varnish-cache/bin/varnishd/cache_hash.c
===================================================================
--- branches/2.0/varnish-cache/bin/varnishd/cache_hash.c	2009-02-06 11:38:07 UTC (rev 3652)
+++ branches/2.0/varnish-cache/bin/varnishd/cache_hash.c	2009-02-06 11:47:24 UTC (rev 3653)
@@ -251,7 +251,6 @@
 	struct http *h;
 	struct objhead *oh;
 	struct object *o, *busy_o, *grace_o;
-	unsigned char sha256[32];
 
 	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
 	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
@@ -259,12 +258,14 @@
 	AN(hash);
 	w = sp->wrk;
 	h = sp->http;
+
+	HSH_Prealloc(sp);
 	if (params->hash_sha256) {
-		SHA256_Final(sha256, sp->wrk->sha256ctx);
+		SHA256_Final(sp->wrk->nobjhead->digest, sp->wrk->sha256ctx);
+		sp->wrk->nobjhead->digest_len = 32;
 		/* WSP(sp, SLT_Debug, "SHA256: <%.32s>", sha256); */
 	}
-
-	HSH_Prealloc(sp);
+	
 	if (sp->objhead != NULL) {
 		CHECK_OBJ_NOTNULL(sp->objhead, OBJHEAD_MAGIC);
 		oh = sp->objhead;

Copied: branches/2.0/varnish-cache/bin/varnishd/hash_critbit.c (from rev 3455, trunk/varnish-cache/bin/varnishd/hash_critbit.c)
===================================================================
--- branches/2.0/varnish-cache/bin/varnishd/hash_critbit.c	                        (rev 0)
+++ branches/2.0/varnish-cache/bin/varnishd/hash_critbit.c	2009-02-06 11:47:24 UTC (rev 3653)
@@ -0,0 +1,418 @@
+/*-
+ * Copyright (c) 2008 Linpro AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ *
+ * A Crit Bit tree based hash
+ */
+
+#include "config.h"
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "shmlog.h"
+#include "cache.h"
+#include "hash_slinger.h"
+
+static struct lock hcb_mtx;
+
+/**********************************************************************
+ * Table for finding out how many bits two bytes have in common,
+ * counting from the MSB towards the LSB.
+ * ie: 
+ *	hcb_bittbl[0x01 ^ 0x22] == 2
+ *	hcb_bittbl[0x10 ^ 0x0b] == 3
+ * 
+ */
+
+static unsigned char hcb_bittbl[256];
+
+static unsigned char
+hcb_bits(unsigned char x, unsigned char y)
+{
+	return hcb_bittbl[x ^ y];
+}
+
+static void
+hcb_build_bittbl(void)
+{
+	unsigned x, y;
+
+	y = 0;
+	for (x = 0; x < 8; x++)
+		for (; y < (1U << x); y++)
+			hcb_bittbl[y] = 8 - x;
+
+	/* Quick asserts for sanity check */
+	assert(hcb_bits(0x34, 0x34) == 8);
+	assert(hcb_bits(0xaa, 0x55) == 0);
+	assert(hcb_bits(0x01, 0x22) == 2);
+	assert(hcb_bits(0x10, 0x0b) == 3);
+}
+
+/**********************************************************************
+ * For space reasons we overload the two pointers with two different
+ * kinds of of pointers.  We cast them to uintptr_t's and abuse the
+ * low two bits to tell them apart, assuming that Varnish will never
+ * run on machines with less than 32bit alignment.
+ *
+ * Asserts will explode if these assumptions are not met.
+ */
+
+struct hcb_y {
+	unsigned short	critbit;
+	unsigned char	ptr;
+	unsigned char	bitmask;
+	uintptr_t	leaf[2];
+};
+
+#define HCB_BIT_NODE		(1<<0)
+#define HCB_BIT_Y		(1<<1)
+
+struct hcb_root {
+	uintptr_t	origo;
+	unsigned	cmps;
+};
+
+static struct hcb_root	hcb_root;
+
+/**********************************************************************
+ * Pointer accessor functions
+ */
+static int
+hcb_is_node(uintptr_t u)
+{
+
+	return (u & HCB_BIT_NODE);
+}
+
+static int
+hcb_is_y(uintptr_t u)
+{
+
+	return (u & HCB_BIT_Y);
+}
+
+static uintptr_t
+hcb_r_node(struct objhead *n)
+{
+
+	assert(!((uintptr_t)n & (HCB_BIT_NODE|HCB_BIT_Y)));
+	return (HCB_BIT_NODE | (uintptr_t)n);
+}
+
+static struct objhead *
+hcb_l_node(uintptr_t u)
+{
+
+	assert(u & HCB_BIT_NODE);
+	assert(!(u & HCB_BIT_Y));
+	return ((struct objhead *)(u & ~HCB_BIT_NODE));
+}
+
+static uintptr_t
+hcb_r_y(struct hcb_y *y)
+{
+
+	assert(!((uintptr_t)y & (HCB_BIT_NODE|HCB_BIT_Y)));
+	return (HCB_BIT_Y | (uintptr_t)y);
+}
+
+static struct hcb_y *
+hcb_l_y(uintptr_t u)
+{
+
+	assert(!(u & HCB_BIT_NODE));
+	assert(u & HCB_BIT_Y);
+	return ((struct hcb_y *)(u & ~HCB_BIT_Y));
+}
+
+/**********************************************************************/
+
+static unsigned
+hcb_crit_bit(const struct objhead *oh1, const struct objhead *oh2, struct hcb_y *y)
+{
+	unsigned u, r;
+
+	for (u = 0; u < oh1->digest_len && oh1->digest[u] == oh2->digest[u]; u++)
+		;
+	r = hcb_bits(oh1->digest[u], oh2->digest[u]);
+	y->ptr = u;
+	y->bitmask = 0x80 >> r;
+	y->critbit = u * 8 + r;
+	return (y->critbit);
+}
+
+/**********************************************************************/
+
+static struct objhead *
+hcb_insert(struct hcb_root *root, struct objhead *oh, int has_lock)
+{
+	uintptr_t *p;
+	struct hcb_y *y, *y2;
+	struct objhead *oh2;
+	unsigned s, s2;
+
+
+	p = &root->origo;
+	if (*p == 0) {
+		if (!has_lock)
+			return (NULL);
+		*p = hcb_r_node(oh);
+		return (oh);
+	}
+
+	while(hcb_is_y(*p)) {
+		y = hcb_l_y(*p);
+		if (y->ptr > oh->digest_len)
+			s = 0;
+		else
+			s = (oh->digest[y->ptr] & y->bitmask) != 0;
+		assert(s < 2);
+		root->cmps++;
+		p = &y->leaf[s];
+	}
+
+	assert(hcb_is_node(*p));
+
+	/* We found a node, does it match ? */
+	oh2 = hcb_l_node(*p);
+	if (oh2->digest_len == oh->digest_len &&
+	    !memcmp(oh2->digest, oh->digest, oh->digest_len))
+		return (oh2);
+
+	if (!has_lock)
+		return (NULL);
+
+	/* Insert */
+
+	y2 = (void*)&oh->u;
+	memset(y2, 0, sizeof *y2);
+	(void)hcb_crit_bit(oh, hcb_l_node(*p), y2);
+	s2 = (oh->digest[y2->ptr] & y2->bitmask) != 0;
+	assert(s2 < 2);
+	y2->leaf[s2] = hcb_r_node(oh);
+	s2 = 1-s2;
+
+	p = &root->origo;
+	assert(*p != 0);
+
+	while(hcb_is_y(*p)) {
+		y = hcb_l_y(*p);
+		if (y->critbit > y2->critbit)
+			break;
+		if (y->ptr > oh->digest_len)
+			s = 0;
+		else
+			s = (oh->digest[y->ptr] & y->bitmask) != 0;
+		assert(s < 2);
+		root->cmps++;
+		p = &y->leaf[s];
+	}
+	y2->leaf[s2] = *p;
+	*p = hcb_r_y(y2);
+	return(oh);
+}
+
+/**********************************************************************/
+
+static void
+hcb_delete(struct hcb_root *r, struct objhead *oh)
+{
+	struct hcb_y *y;
+	uintptr_t *p;
+	unsigned s;
+
+	if (r->origo == hcb_r_node(oh)) {
+		r->origo = 0;
+		return;
+	}
+	p = &r->origo;
+	assert(hcb_is_y(*p));
+	
+	y = NULL;
+	while(hcb_is_y(*p)) {
+		y = hcb_l_y(*p);
+		if (y->ptr > oh->digest_len)
+			s = 0;
+		else
+			s = (oh->digest[y->ptr] & y->bitmask) != 0;
+		assert(s < 2);
+		if (y->leaf[s] == hcb_r_node(oh)) {
+			*p = y->leaf[1 - s];
+			y->leaf[0] = 0;
+			y->leaf[1] = 0;
+			return;
+		}
+		r->cmps++;
+		p = &y->leaf[s];
+	}
+abort();
+	*p = y->leaf[1 - s];
+}
+
+/**********************************************************************/
+
+static void
+dumptree(uintptr_t p, int indent, FILE *fd)
+{
+	int i;
+	const struct objhead *oh;
+	const struct hcb_y *y;
+
+	if (p == 0)
+		return;
+	if (hcb_is_node(p)) {
+		oh = hcb_l_node(p);
+		fprintf(fd, "%*.*sN %u %u r%d <%02x%02x%02x...> <%s>\n",
+		    indent, indent, "", oh->digest_len, indent / 2,
+		    oh->refcnt,
+		    oh->digest[0], oh->digest[1], oh->digest[2],
+		    oh->hash);
+		return;
+	}
+	assert(hcb_is_y(p));
+	y = hcb_l_y(p);
+	fprintf(fd, "%*.*sY c %u p %u b %02x i %d\n",
+	    indent, indent, "",
+	    y->critbit, y->ptr, y->bitmask, indent / 2);
+	indent += 2;
+	for (i = 0; i < 2; i++)
+		dumptree(y->leaf[i], indent, fd);
+}
+
+static void
+dump(const struct hcb_root *root, FILE *fd)
+{
+	fprintf(fd, "-------------\n");
+	dumptree(root->origo, 0, fd);
+	fprintf(fd, "-------------\n");
+	fflush(fd);
+}
+
+
+/**********************************************************************/
+
+static void
+hcb_start(void)
+{
+	struct objhead *oh = NULL;
+
+	(void)oh;
+	assert(params->hash_sha256);
+	assert(sizeof(struct hcb_y) <= sizeof(oh->u));
+	memset(&hcb_root, 0, sizeof hcb_root);
+	hcb_build_bittbl();
+	Lck_New(&hcb_mtx);
+}
+
+static int
+hcb_deref(struct objhead *oh)
+{
+	int r;
+	struct hcb_y *y;
+
+	r = 1;
+	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+	Lck_Lock(&oh->mtx);
+	if (--oh->refcnt == 0) {
+		Lck_Lock(&hcb_mtx);
+		hcb_delete(&hcb_root, oh);
+		y = (void*)&oh->u;
+		if (y->leaf[0] == 0 && y->leaf[1] == 0)
+			r = 0;
+		else {
+			/* XXX: on waiting list */
+		}
+		Lck_Unlock(&hcb_mtx);
+	}
+	Lck_Unlock(&oh->mtx);
+	if (0) {
+		fprintf(stderr, "%s %d %d <%s>\n", __func__, __LINE__, r, oh->hash);
+		dump(&hcb_root, stderr);
+	}
+	return (r);
+}
+
+static struct objhead *
+hcb_lookup(const struct sess *sp, struct objhead *noh)
+{
+	struct objhead *oh;
+	
+	assert(params->hash_sha256);
+	oh =  hcb_insert(&hcb_root, noh, 0);
+	if (oh != NULL) {
+		/* Assert that we didn't muck with the tree without lock */
+		assert(oh != noh);
+		Lck_Lock(&oh->mtx);
+		oh->refcnt++;
+		Lck_Unlock(&oh->mtx);
+		VSL_stats->hcb_nolock++;
+		if (0) {
+			fprintf(stderr, "%s %d\n", __func__, __LINE__);
+			dump(&hcb_root, stderr);
+		}
+		return (oh);
+	}
+
+	/*
+	 * Try again, holding lock and fully ready objhead, so that if
+	 * somebody else beats us back, they do not get surprised.
+	 */
+	HSH_Copy(sp, noh);
+	Lck_Lock(&hcb_mtx);
+	oh =  hcb_insert(&hcb_root, noh, 1);
+	if (oh == noh) {
+		VSL_stats->hcb_insert++;
+		if (0) {
+			fprintf(stderr, "%s %d\n", __func__, __LINE__);
+			dump(&hcb_root, stderr);
+		}
+	} else {
+		free(noh->hash);
+		noh->hash = NULL;
+		noh->hashlen = 0;
+		VSL_stats->hcb_lock++;
+		if (0) {
+			fprintf(stderr, "%s %d\n", __func__, __LINE__);
+			dump(&hcb_root, stderr);
+		}
+	}
+	Lck_Unlock(&hcb_mtx);
+	return (oh);
+}
+
+
+struct hash_slinger hcb_slinger = {
+	.magic  =       SLINGER_MAGIC,
+	.name   =       "critbit",
+	.start  =       hcb_start,
+	.lookup =       hcb_lookup,
+	.deref  =       hcb_deref,
+};

Modified: branches/2.0/varnish-cache/bin/varnishd/hash_slinger.h
===================================================================
--- branches/2.0/varnish-cache/bin/varnishd/hash_slinger.h	2009-02-06 11:38:07 UTC (rev 3652)
+++ branches/2.0/varnish-cache/bin/varnishd/hash_slinger.h	2009-02-06 11:47:24 UTC (rev 3653)
@@ -73,14 +73,24 @@
 	VTAILQ_HEAD(,object)	objects;
 	char			*hash;
 	unsigned		hashlen;
+	unsigned char		digest[32];
+	unsigned char		digest_len;
 	VTAILQ_HEAD(, sess)	waitinglist;
 
-	/*------------------------------------------------------------
-	 * The fields below are for the sole private use of the hash 
-	 * implementation.
+	/*----------------------------------------------------
+	 * The fields below are for the sole private use of
+	 * the hash implementation(s).
 	 */
-	VTAILQ_ENTRY(objhead)	hoh_list;
-	void			*hoh_head;
-	unsigned		hoh_digest;
+	union {
+		void		*filler[3];
+		struct {
+			VTAILQ_ENTRY(objhead)	u_n_hoh_list;
+			void			*u_n_hoh_head;
+			unsigned		u_n_hoh_digest;
+		} n;
+	} u;
+#define hoh_list u.n.u_n_hoh_list
+#define hoh_head u.n.u_n_hoh_head
+#define hoh_digest u.n.u_n_hoh_digest
 };
 #endif /* VARNISH_CACHE_CHILD */

Modified: branches/2.0/varnish-cache/bin/varnishd/mgt_param.c
===================================================================
--- branches/2.0/varnish-cache/bin/varnishd/mgt_param.c	2009-02-06 11:38:07 UTC (rev 3652)
+++ branches/2.0/varnish-cache/bin/varnishd/mgt_param.c	2009-02-06 11:47:24 UTC (rev 3653)
@@ -713,7 +713,7 @@
 	{ "hash_sha256", tweak_bool, &master.hash_sha256, 0, 0,
 		"Use SHA256 compression of hash-strings",
 		0,
-		"off", "bool" },
+		"on", "bool" },
 	{ "log_hashstring", tweak_bool, &master.log_hash, 0, 0,
 		"Log the hash string to shared memory log.\n",
 		0,

Modified: branches/2.0/varnish-cache/bin/varnishd/varnishd.c
===================================================================
--- branches/2.0/varnish-cache/bin/varnishd/varnishd.c	2009-02-06 11:38:07 UTC (rev 3652)
+++ branches/2.0/varnish-cache/bin/varnishd/varnishd.c	2009-02-06 11:47:24 UTC (rev 3653)
@@ -59,6 +59,7 @@
 
 #include "vsb.h"
 #include "vpf.h"
+#include "vsha256.h"
 
 #include "cli.h"
 #include "cli_priv.h"
@@ -157,11 +158,13 @@
 
 extern struct hash_slinger hsl_slinger;
 extern struct hash_slinger hcl_slinger;
+extern struct hash_slinger hcb_slinger;
 
 static const struct choice hsh_choice[] = {
 	{ "classic",		&hcl_slinger },
 	{ "simple",		&hsl_slinger },
 	{ "simple_list",	&hsl_slinger },	/* backwards compat */
+	{ "critbit",		&hcb_slinger },
 	{ NULL,			NULL }
 };
 
@@ -450,6 +453,11 @@
 	assert(TIM_parse("Sunday, 06-Nov-94 08:49:37 GMT") == 784111777);
 	assert(TIM_parse("Sun Nov  6 08:49:37 1994") == 784111777);
 
+	/*
+	 * Check that our SHA256 works
+	 */
+	SHA256_Test();
+
 	memset(cli, 0, sizeof cli);
 	cli[0].sb = vsb_newauto();
 	XXXAN(cli[0].sb);

Modified: branches/2.0/varnish-cache/include/stat_field.h
===================================================================
--- branches/2.0/varnish-cache/include/stat_field.h	2009-02-06 11:38:07 UTC (rev 3652)
+++ branches/2.0/varnish-cache/include/stat_field.h	2009-02-06 11:47:24 UTC (rev 3653)
@@ -126,3 +126,7 @@
 MAC_STAT(n_purge_obj_test,	uint64_t, 'a', "N objects tested")
 MAC_STAT(n_purge_re_test,	uint64_t, 'a', "N regexps tested against")
 MAC_STAT(n_purge_dups,		uint64_t, 'a', "N duplicate purges removed")
+
+MAC_STAT(hcb_nolock,		uint64_t, 'a', "HCB Lookups without lock")
+MAC_STAT(hcb_lock,		uint64_t, 'a', "HCB Lookups with lock")
+MAC_STAT(hcb_insert,		uint64_t, 'a', "HCB Inserts")



More information about the varnish-commit mailing list