r884 - trunk/varnish-cache/bin/varnishd

phk at projects.linpro.no phk at projects.linpro.no
Tue Aug 22 09:52:33 CEST 2006


Author: phk
Date: 2006-08-22 09:52:33 +0200 (Tue, 22 Aug 2006)
New Revision: 884

Modified:
   trunk/varnish-cache/bin/varnishd/hash_classic.c
Log:
Rework the classic hasher to reduce lock contention and CPU usage.

Make nbuckets=nhash mandatory.

Order hash lists by the length of key instead of the key contents,
comparing the length is much faster.

Also compare disgest before we take the expensive content compare.

Use memcmp() for content compare instead of strcmp().

Use a two-pass algorithm for inserts to lower mutex contention.



Modified: trunk/varnish-cache/bin/varnishd/hash_classic.c
===================================================================
--- trunk/varnish-cache/bin/varnishd/hash_classic.c	2006-08-22 07:24:07 UTC (rev 883)
+++ trunk/varnish-cache/bin/varnishd/hash_classic.c	2006-08-22 07:52:33 UTC (rev 884)
@@ -11,6 +11,7 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include <shmlog.h>
 #include <cache.h>
 
 #if defined(HASH_CLASSIC_MD5) && !defined(HAVE_MD5)
@@ -28,20 +29,24 @@
 	unsigned		magic;
 #define HCL_ENTRY_MAGIC		0x0ba707bf
 	TAILQ_ENTRY(hcl_entry)	list;
-	char			*key1;
-	char			*key2;
+	struct hcl_hd		*head;
+	char			*key;
+	unsigned		klen;
 	struct objhead		*oh;
 	unsigned		refcnt;
+	unsigned		digest;
 	unsigned		hash;
-	unsigned		mtx;
 };
 
-TAILQ_HEAD(hcl_head, hcl_entry);
+struct hcl_hd {
+	unsigned		magic;
+#define HCL_HEAD_MAGIC		0x0f327016
+	TAILQ_HEAD(, hcl_entry)	head;
+	pthread_mutex_t		mtx;
+};
 
-static struct hcl_head *hcl_head;
-static unsigned hcl_nhash = 4096;
-static unsigned hcl_nmtx = 4096;
-static pthread_mutex_t *hcl_mutex;
+static unsigned			hcl_nhash = 4096;
+static struct hcl_hd		*hcl_head;
 
 /*--------------------------------------------------------------------*/
 
@@ -118,30 +123,13 @@
 hcl_init(const char *p)
 {
 	int i;
-	unsigned u1, u2;
+	unsigned u;
 
-	i = sscanf(p, "%u,%u", &u1, &u2);
-	if (i <= 0)
+	i = sscanf(p, "%u", &u);
+	if (i <= 0 || u == 0)
 		return (0);
-	if (u1 == 0 || (i == 2 && (u2 == 0 || u2 > u1))) {
-		fprintf(stderr, "Invallid parameters to hash \"classic\":\n");
-		fprintf(stderr,
-		    "\t-h classic,<bucket count>[,<buckets per mutex>]\n");
-		return (1);
-	}
-	hcl_nhash = u1;
-	if (i == 1) {
-		hcl_nmtx = hcl_nhash;
-		if (hcl_nmtx <  1)
-			hcl_nmtx = 1;
-		return(0);
-	} else {
-		hcl_nmtx = hcl_nhash / u2;
-		if (hcl_nmtx < 1)
-			hcl_nmtx = 1;
-	}
-	fprintf(stderr, "Classic hash: %u buckets %u mutexes\n",
-	    hcl_nhash, hcl_nmtx);
+	hcl_nhash = u;
+	fprintf(stderr, "Classic hash: %u buckets\n", hcl_nhash);
 	return (0);
 }
 
@@ -157,15 +145,12 @@
 
 	hcl_head = calloc(sizeof *hcl_head, hcl_nhash);
 	assert(hcl_head != NULL);
-	hcl_mutex = calloc(sizeof *hcl_mutex, hcl_nmtx);
-	assert(hcl_mutex != NULL);
 
-
-	for (u = 0; u < hcl_nhash; u++)
-		TAILQ_INIT(&hcl_head[u]);
-
-	for (u = 0; u < hcl_nmtx; u++)
-		AZ(pthread_mutex_init(&hcl_mutex[u], NULL));
+	for (u = 0; u < hcl_nhash; u++) {
+		TAILQ_INIT(&hcl_head[u].head);
+		AZ(pthread_mutex_init(&hcl_head[u].mtx, NULL));
+		hcl_head[u].magic = HCL_HEAD_MAGIC;
+	}
 }
 
 /*--------------------------------------------------------------------
@@ -173,13 +158,16 @@
  * If nobj != NULL and the lookup does not find key, nobj is inserted.
  * If nobj == NULL and the lookup does not find key, NULL is returned.
  * A reference to the returned object is held.
+ * We use a two-pass algorithm to handle inserts as they are quite
+ * rare and collisions even rarer.
  */
 
 static struct objhead *
 hcl_lookup(const char *key1, const char *key2, struct objhead *noh)
 {
 	struct hcl_entry *he, *he2;
-	unsigned u1, u2;
+	struct hcl_hd *hp;
+	unsigned u1, digest, kl1, kl2, kl, r;
 	int i;
 #ifdef HASH_CLASSIC_MD5
 	MD5_CTX c;
@@ -194,60 +182,72 @@
 	MD5Update(&c, "", 1);
 	MD5Update(&c, key2, strlen(key2));
 	MD5Final(md5, &c);
-	memcpy(&u1, md5, sizeof u1);
-	memcpy(&u2, md5 + sizeof u1, sizeof u2);
+	memcpy(&digest, md5, sizeof digest);
 #else
-	u1 = u2 = crc32(key1, key2);
+	digest = crc32(key1, key2);
 #endif
 
-	u1 %= hcl_nhash;
-	u2 %= hcl_nmtx;
+	u1 = digest % hcl_nhash;
+	hp = &hcl_head[u1];
+	kl1 = strlen(key1) + 1;		/* Incl '/0' */
+	kl2 = strlen(key2);
+	kl = kl1 + kl2;
+	he2 = NULL;
 
-	AZ(pthread_mutex_lock(&hcl_mutex[u2]));
-	TAILQ_FOREACH(he, &hcl_head[u1], list) {
-		CHECK_OBJ_NOTNULL(he, HCL_ENTRY_MAGIC);
-		i = strcmp(key1, he->key1);
-		if (i < 0)
-			continue;
-		if (i > 0)
-			break;
-		i = strcmp(key2, he->key2);
-		if (i < 0)
-			continue;
-		if (i > 0)
-			break;
-		he->refcnt++;
-		noh = he->oh;
-		noh->hashpriv = he;
-		AZ(pthread_mutex_unlock(&hcl_mutex[u2]));
-		return (noh);
-	}
-	if (noh == NULL) {
-		AZ(pthread_mutex_unlock(&hcl_mutex[u2]));
-		return (NULL);
-	}
-	i = sizeof *he2 + strlen(key1) + 1 + strlen(key2) + 1;
-	he2 = calloc(i, 1);
-	assert(he2 != NULL);
-	he2->magic = HCL_ENTRY_MAGIC;
-	he2->oh = noh;
-	he2->refcnt = 1;
-	he2->hash = u1;
-	he2->mtx = u2;
+	for (r = 0; r < 2; r++ ) {
+		AZ(pthread_mutex_lock(&hp->mtx));
+		TAILQ_FOREACH(he, &hp->head, list) {
+			CHECK_OBJ_NOTNULL(he, HCL_ENTRY_MAGIC);
+			if (kl < he->klen)
+				continue;
+			if (kl > he->klen)
+				break;
+			if (he->digest != digest)
+				continue;
+			if (memcmp(he->key, key1, kl1))
+				continue;
+			if (memcmp(he->key + kl1, key2, kl2))
+				continue;
+			he->refcnt++;
+			noh = he->oh;
+			AZ(pthread_mutex_unlock(&hp->mtx));
+			if (he2 != NULL)
+				free(he2);
+			return (noh);
+		}
+		if (noh == NULL) {
+			AZ(pthread_mutex_unlock(&hp->mtx));
+			return (NULL);
+		}
+		if (he2 != NULL) {
+			if (he != NULL)
+				TAILQ_INSERT_BEFORE(he, he2, list);
+			else
+				TAILQ_INSERT_TAIL(&hp->head, he2, list);
+			he2->refcnt++;
+			noh = he2->oh;
+			AZ(pthread_mutex_unlock(&hp->mtx));
+			return (noh);
+		}
+		AZ(pthread_mutex_unlock(&hp->mtx));
 
-	he2->key1 = (void*)(he2 + 1);
-	strcpy(he2->key1, key1);
-	he2->key2 = strchr(he2->key1, '\0');
-	he2->key2++;
-	strcpy(he2->key2, key2);
+		i = sizeof *he2 + kl;
+		he2 = calloc(i, 1);
+		assert(he2 != NULL);
+		he2->magic = HCL_ENTRY_MAGIC;
+		he2->oh = noh;
+		he2->digest = digest;
+		he2->hash = u1;
+		he2->head = hp;
+		he2->klen = kl;
+		noh->hashpriv = he2;
 
-	noh->hashpriv = he2;
-	if (he != NULL)
-		TAILQ_INSERT_BEFORE(he, he2, list);
-	else
-		TAILQ_INSERT_TAIL(&hcl_head[u1], he2, list);
-	AZ(pthread_mutex_unlock(&hcl_mutex[u2]));
-	return (noh);
+		he2->key = (void*)(he2 + 1);
+		memcpy(he2->key, key1, kl1);
+		memcpy(he2->key + kl1, key2, kl2);
+	}
+	assert(he2 == NULL);		/* FlexeLint */
+	INCOMPL();
 }
 
 /*--------------------------------------------------------------------
@@ -258,20 +258,21 @@
 hcl_deref(struct objhead *oh)
 {
 	struct hcl_entry *he;
-	unsigned mtx;
+	struct hcl_hd *hp;
 
 	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
 	CAST_OBJ_NOTNULL(he, oh->hashpriv, HCL_ENTRY_MAGIC);
+	hp = he->head;
+	CHECK_OBJ_NOTNULL(hp, HCL_HEAD_MAGIC);
 	assert(he->refcnt > 0);
-	assert(he->mtx < hcl_nmtx);
 	assert(he->hash < hcl_nhash);
-	mtx = he->mtx;
-	AZ(pthread_mutex_lock(&hcl_mutex[mtx]));
+	assert(hp == &hcl_head[he->hash]);
+	AZ(pthread_mutex_lock(&hp->mtx));
 	if (--he->refcnt == 0)
-		TAILQ_REMOVE(&hcl_head[he->hash], he, list);
+		TAILQ_REMOVE(&hp->head, he, list);
 	else
 		he = NULL;
-	AZ(pthread_mutex_unlock(&hcl_mutex[mtx]));
+	AZ(pthread_mutex_unlock(&hp->mtx));
 	if (he == NULL)
 		return (1);
 	free(he);




More information about the varnish-commit mailing list