vppinfra: bihash improvements

Template instances can allocate BIHASH_KVP_PER_PAGE data records
tangent to the bucket, to remove a dependent read / prefetch.

Template instances can ask for immediate memory allocation, to avoid
several branches in the lookup path.

Clean up l2 fib, gpb plugin codes: use clib_bihash_get_bucket(...)

Use hugepages for bihash allocation arenas

Type: improvement

Signed-off-by: Dave Barach <dave@barachs.net>
Signed-off-by: Damjan Marion <damarion@cisco.com>
Change-Id: I92fc11bc58e48d84e2d61f44580916dd1c56361c
diff --git a/src/plugins/gbp/gbp_endpoint.c b/src/plugins/gbp/gbp_endpoint.c
index 9ef0890..e1a810c 100644
--- a/src/plugins/gbp/gbp_endpoint.c
+++ b/src/plugins/gbp/gbp_endpoint.c
@@ -1398,8 +1398,8 @@
 	  last_start = vlib_time_now (vm);
 	}
 
-      b = &gte_table->buckets[i];
-      if (b->offset == 0)
+      b = clib_bihash_get_bucket_16_8 (gte_table, i);
+      if (clib_bihash_bucket_is_empty_16_8 (b))
 	continue;
       v = clib_bihash_get_value_16_8 (gte_table, b->offset);
 
@@ -1416,7 +1416,7 @@
 	       * Note: we may have just freed the bucket's backing
 	       * storage, so check right here...
 	       */
-	      if (b->offset == 0)
+	      if (clib_bihash_bucket_is_empty_16_8 (b))
 		goto doublebreak;
 	    }
 	  v++;
@@ -1453,8 +1453,8 @@
 	  last_start = vlib_time_now (vm);
 	}
 
-      b = &gte_table->buckets[i];
-      if (b->offset == 0)
+      b = clib_bihash_get_bucket_24_8 (gte_table, i);
+      if (clib_bihash_bucket_is_empty_24_8 (b))
 	continue;
       v = clib_bihash_get_value_24_8 (gte_table, b->offset);
 
@@ -1471,7 +1471,7 @@
 	       * Note: we may have just freed the bucket's backing
 	       * storage, so check right here...
 	       */
-	      if (b->offset == 0)
+	      if (clib_bihash_bucket_is_empty_24_8 (b))
 		goto doublebreak;
 	    }
 	  v++;
diff --git a/src/vnet/l2/l2_fib.c b/src/vnet/l2/l2_fib.c
index 160e4e6..983e021 100644
--- a/src/vnet/l2/l2_fib.c
+++ b/src/vnet/l2/l2_fib.c
@@ -1028,10 +1028,11 @@
 
       if (i < (h->nbuckets - 3))
 	{
-	  BVT (clib_bihash_bucket) * b = &h->buckets[i + 3];
+	  BVT (clib_bihash_bucket) * b =
+	    BV (clib_bihash_get_bucket) (h, i + 3);
 	  CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD);
-	  b = &h->buckets[i + 1];
-	  if (b->offset)
+	  b = BV (clib_bihash_get_bucket) (h, i + 1);
+	  if (!BV (clib_bihash_bucket_is_empty) (b))
 	    {
 	      BVT (clib_bihash_value) * v =
 		BV (clib_bihash_get_value) (h, b->offset);
@@ -1039,8 +1040,8 @@
 	    }
 	}
 
-      BVT (clib_bihash_bucket) * b = &h->buckets[i];
-      if (b->offset == 0)
+      BVT (clib_bihash_bucket) * b = BV (clib_bihash_get_bucket) (h, i);
+      if (BV (clib_bihash_bucket_is_empty) (b))
 	continue;
       BVT (clib_bihash_value) * v = BV (clib_bihash_get_value) (h, b->offset);
       for (j = 0; j < (1 << b->log2_pages); j++)
@@ -1146,7 +1147,7 @@
 	       * Note: we may have just freed the bucket's backing
 	       * storage, so check right here...
 	       */
-	      if (b->offset == 0)
+	      if (BV (clib_bihash_bucket_is_empty) (b))
 		goto doublebreak;
 	    }
 	  v++;
diff --git a/src/vppinfra/bihash_16_8.h b/src/vppinfra/bihash_16_8.h
index b6b0766..1815a52 100644
--- a/src/vppinfra/bihash_16_8.h
+++ b/src/vppinfra/bihash_16_8.h
@@ -16,9 +16,15 @@
 #undef BIHASH_KVP_PER_PAGE
 #undef BIHASH_32_64_SVM
 #undef BIHASH_ENABLE_STATS
+#undef BIHASH_KVP_AT_BUCKET_LEVEL
+#undef BIHASH_LAZY_INSTANTIATE
+#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
 
 #define BIHASH_TYPE _16_8
 #define BIHASH_KVP_PER_PAGE 4
+#define BIHASH_KVP_AT_BUCKET_LEVEL 1
+#define BIHASH_LAZY_INSTANTIATE 0
+#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 2
 
 #ifndef __included_bihash_16_8_h__
 #define __included_bihash_16_8_h__
diff --git a/src/vppinfra/bihash_16_8_32.h b/src/vppinfra/bihash_16_8_32.h
index e66954f..9453f88 100644
--- a/src/vppinfra/bihash_16_8_32.h
+++ b/src/vppinfra/bihash_16_8_32.h
@@ -16,10 +16,15 @@
 #undef BIHASH_KVP_PER_PAGE
 #undef BIHASH_32_64_SVM
 #undef BIHASH_ENABLE_STATS
-
+#undef BIHASH_KVP_AT_BUCKET_LEVEL
+#undef BIHASH_LAZY_INSTANTIATE
+#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
 
 #define BIHASH_TYPE _16_8_32
 #define BIHASH_KVP_PER_PAGE 4
+#define BIHASH_KVP_AT_BUCKET_LEVEL 0
+#define BIHASH_LAZY_INSTANTIATE 1
+#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 1
 
 #define BIHASH_32_64_SVM 1
 
diff --git a/src/vppinfra/bihash_24_8.h b/src/vppinfra/bihash_24_8.h
index 463521d..33199bf 100644
--- a/src/vppinfra/bihash_24_8.h
+++ b/src/vppinfra/bihash_24_8.h
@@ -16,9 +16,15 @@
 #undef BIHASH_KVP_PER_PAGE
 #undef BIHASH_32_64_SVM
 #undef BIHASH_ENABLE_STATS
+#undef BIHASH_KVP_AT_BUCKET_LEVEL
+#undef BIHASH_LAZY_INSTANTIATE
+#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
 
 #define BIHASH_TYPE _24_8
 #define BIHASH_KVP_PER_PAGE 4
+#define BIHASH_KVP_AT_BUCKET_LEVEL 0
+#define BIHASH_LAZY_INSTANTIATE 1
+#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 1
 
 #ifndef __included_bihash_24_8_h__
 #define __included_bihash_24_8_h__
diff --git a/src/vppinfra/bihash_40_8.h b/src/vppinfra/bihash_40_8.h
index b50e5eb..9cd2371 100644
--- a/src/vppinfra/bihash_40_8.h
+++ b/src/vppinfra/bihash_40_8.h
@@ -17,9 +17,15 @@
 #undef BIHASH_KVP_PER_PAGE
 #undef BIHASH_32_64_SVM
 #undef BIHASH_ENABLE_STATS
+#undef BIHASH_KVP_AT_BUCKET_LEVEL
+#undef BIHASH_LAZY_INSTANTIATE
+#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
 
 #define BIHASH_TYPE _40_8
 #define BIHASH_KVP_PER_PAGE 4
+#define BIHASH_KVP_AT_BUCKET_LEVEL 0
+#define BIHASH_LAZY_INSTANTIATE 1
+#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 1
 
 #ifndef __included_bihash_40_8_h__
 #define __included_bihash_40_8_h__
diff --git a/src/vppinfra/bihash_48_8.h b/src/vppinfra/bihash_48_8.h
index 2a6381f..54fd709 100644
--- a/src/vppinfra/bihash_48_8.h
+++ b/src/vppinfra/bihash_48_8.h
@@ -17,9 +17,15 @@
 #undef BIHASH_KVP_PER_PAGE
 #undef BIHASH_32_64_SVM
 #undef BIHASH_ENABLE_STATS
+#undef BIHASH_KVP_AT_BUCKET_LEVEL
+#undef BIHASH_LAZY_INSTANTIATE
+#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
 
 #define BIHASH_TYPE _48_8
 #define BIHASH_KVP_PER_PAGE 4
+#define BIHASH_KVP_AT_BUCKET_LEVEL 0
+#define BIHASH_LAZY_INSTANTIATE 1
+#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 1
 
 #ifndef __included_bihash_48_8_h__
 #define __included_bihash_48_8_h__
diff --git a/src/vppinfra/bihash_8_8.h b/src/vppinfra/bihash_8_8.h
index a4a18a1..2348907 100644
--- a/src/vppinfra/bihash_8_8.h
+++ b/src/vppinfra/bihash_8_8.h
@@ -16,9 +16,15 @@
 #undef BIHASH_KVP_PER_PAGE
 #undef BIHASH_32_64_SVM
 #undef BIHASH_ENABLE_STATS
+#undef BIHASH_KVP_AT_BUCKET_LEVEL
+#undef BIHASH_LAZY_INSTANTIATE
+#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
 
 #define BIHASH_TYPE _8_8
-#define BIHASH_KVP_PER_PAGE 4
+#define BIHASH_KVP_PER_PAGE 7
+#define BIHASH_KVP_AT_BUCKET_LEVEL 1
+#define BIHASH_LAZY_INSTANTIATE 0
+#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 2
 
 #ifndef __included_bihash_8_8_h__
 #define __included_bihash_8_8_h__
diff --git a/src/vppinfra/bihash_8_8_stats.h b/src/vppinfra/bihash_8_8_stats.h
index a6c947a..5aceb1b 100644
--- a/src/vppinfra/bihash_8_8_stats.h
+++ b/src/vppinfra/bihash_8_8_stats.h
@@ -16,10 +16,16 @@
 #undef BIHASH_KVP_PER_PAGE
 #undef BIHASH_32_64_SVM
 #undef BIHASH_ENABLE_STATS
+#undef BIHASH_KVP_AT_BUCKET_LEVEL
+#undef BIHASH_LAZY_INSTANTIATE
+#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
 
 #define BIHASH_TYPE _8_8_stats
 #define BIHASH_KVP_PER_PAGE 4
 #define BIHASH_ENABLE_STATS 1
+#define BIHASH_KVP_AT_BUCKET_LEVEL 0
+#define BIHASH_LAZY_INSTANTIATE 1
+#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 1
 
 #ifndef __included_bihash_8_8_stats_h__
 #define __included_bihash_8_8__stats_h__
diff --git a/src/vppinfra/bihash_doc.h b/src/vppinfra/bihash_doc.h
index da8c832..b4b6a4a 100644
--- a/src/vppinfra/bihash_doc.h
+++ b/src/vppinfra/bihash_doc.h
@@ -81,6 +81,7 @@
   uword alloc_arena;		      /**< memory allocation arena  */
   uword alloc_arena_next;	      /**< first available mem chunk */
   uword alloc_arena_size;	      /**< size of the arena */
+  uword alloc_arena_mapped;	      /**< size of mapped memory in the arena */
 } clib_bihash_t;
 
 /** Get pointer to value page given its clib mheap offset */
diff --git a/src/vppinfra/bihash_template.c b/src/vppinfra/bihash_template.c
index 471251d..89bfc8b 100644
--- a/src/vppinfra/bihash_template.c
+++ b/src/vppinfra/bihash_template.c
@@ -15,6 +15,10 @@
 
 /** @cond DOCUMENTATION_IS_IN_BIHASH_DOC_H */
 
+#ifndef MAP_HUGE_SHIFT
+#define MAP_HUGE_SHIFT 26
+#endif
+
 static inline void *BV (alloc_aligned) (BVT (clib_bihash) * h, uword nbytes)
 {
   uword rv;
@@ -29,6 +33,35 @@
   if (alloc_arena_next (h) > alloc_arena_size (h))
     os_out_of_memory ();
 
+  if (alloc_arena_next (h) > alloc_arena_mapped (h))
+    {
+      void *base, *rv;
+      uword alloc = alloc_arena_next (h) - alloc_arena_mapped (h);
+      int mmap_flags = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS;
+      int mmap_flags_huge = (mmap_flags | MAP_HUGETLB |
+			     BIHASH_LOG2_HUGEPAGE_SIZE << MAP_HUGE_SHIFT);
+
+      /* new allocation is 25% of existing one */
+      if (alloc_arena_mapped (h) >> 2 > alloc)
+	alloc = alloc_arena_mapped (h) >> 2;
+
+      /* round allocation to page size */
+      alloc = round_pow2 (alloc, 1 << BIHASH_LOG2_HUGEPAGE_SIZE);
+
+      base = (void *) (uword) (alloc_arena (h) + alloc_arena_mapped (h));
+
+      rv = mmap (base, alloc, PROT_READ | PROT_WRITE, mmap_flags_huge, -1, 0);
+
+      /* fallback - maybe we are still able to allocate normal pages */
+      if (rv == MAP_FAILED)
+	rv = mmap (base, alloc, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
+
+      if (rv == MAP_FAILED)
+	os_out_of_memory ();
+
+      alloc_arena_mapped (h) += alloc;
+    }
+
   return (void *) (uword) (rv + alloc_arena (h));
 }
 
@@ -36,12 +69,43 @@
 {
   uword bucket_size;
 
-  alloc_arena (h) = (uword) clib_mem_vm_alloc (h->memory_size);
+  alloc_arena (h) = clib_mem_vm_reserve (0, h->memory_size,
+					 BIHASH_LOG2_HUGEPAGE_SIZE);
+  if (alloc_arena (h) == ~0)
+    os_out_of_memory ();
   alloc_arena_next (h) = 0;
   alloc_arena_size (h) = h->memory_size;
+  alloc_arena_mapped (h) = 0;
 
   bucket_size = h->nbuckets * sizeof (h->buckets[0]);
+
+  if (BIHASH_KVP_AT_BUCKET_LEVEL)
+    bucket_size +=
+      h->nbuckets * BIHASH_KVP_PER_PAGE * sizeof (BVT (clib_bihash_kv));
+
   h->buckets = BV (alloc_aligned) (h, bucket_size);
+
+  if (BIHASH_KVP_AT_BUCKET_LEVEL)
+    {
+      int i;
+      BVT (clib_bihash_bucket) * b;
+
+      b = h->buckets;
+
+      for (i = 0; i < h->nbuckets; i++)
+	{
+	  b->offset = BV (clib_bihash_get_offset) (h, (void *) (b + 1));
+	  b->refcnt = 1;
+	  /* Mark all elements free */
+	  clib_memset ((b + 1), 0xff,
+		       BIHASH_KVP_PER_PAGE * sizeof (BVT (clib_bihash_kv)));
+
+	  /* Compute next bucket start address */
+	  b = (void *) (((uword) b) + sizeof (*b) +
+			(BIHASH_KVP_PER_PAGE *
+			 sizeof (BVT (clib_bihash_kv))));
+	}
+    }
   CLIB_MEMORY_BARRIER ();
   h->instantiated = 1;
 }
@@ -94,7 +158,9 @@
 					  CLIB_CACHE_LINE_BYTES);
   h->alloc_lock[0] = 0;
 
+#if BIHASH_LAZY_INSTANTIATE
   if (a->instantiate_immediately)
+#endif
     BV (clib_bihash_instantiate) (h);
 }
 
@@ -505,7 +571,7 @@
   BV (clib_bihash_lock_bucket) (b);
 
   /* First elt in the bucket? */
-  if (BV (clib_bihash_bucket_is_empty) (b))
+  if (BIHASH_KVP_AT_BUCKET_LEVEL == 0 && BV (clib_bihash_bucket_is_empty) (b))
     {
       if (is_add == 0)
 	{
@@ -620,6 +686,24 @@
 	      if (PREDICT_TRUE (b->refcnt > 1))
 		{
 		  b->refcnt--;
+		  /* Switch back to the bucket-level kvp array? */
+		  if (BIHASH_KVP_AT_BUCKET_LEVEL && b->refcnt == 1
+		      && b->log2_pages > 0)
+		    {
+		      tmp_b.as_u64 = b->as_u64;
+		      b->offset = BV (clib_bihash_get_offset)
+			(h, (void *) (b + 1));
+		      b->linear_search = 0;
+		      b->log2_pages = 0;
+		      /* Clean up the bucket-level kvp array */
+		      clib_memset
+			((b + 1), 0xff,
+			 BIHASH_KVP_PER_PAGE * sizeof (BVT (clib_bihash_kv)));
+		      BV (clib_bihash_unlock_bucket) (b);
+		      BV (clib_bihash_increment_stat) (h, BIHASH_STAT_del, 1);
+		      goto free_backing_store;
+		    }
+
 		  BV (clib_bihash_unlock_bucket) (b);
 		  BV (clib_bihash_increment_stat) (h, BIHASH_STAT_del, 1);
 		  return (0);
@@ -633,6 +717,7 @@
 		  /* Kill and unlock the bucket */
 		  b->as_u64 = 0;
 
+		free_backing_store:
 		  /* And free the backing storage */
 		  BV (clib_bihash_alloc_lock) (h);
 		  /* Note: v currently points into the middle of the bucket */
@@ -726,14 +811,30 @@
   tmp_b.log2_pages = new_log2_pages;
   tmp_b.offset = BV (clib_bihash_get_offset) (h, save_new_v);
   tmp_b.linear_search = mark_bucket_linear;
-  tmp_b.refcnt = h->saved_bucket.refcnt + 1;
+#if BIHASH_KVP_AT_BUCKET_LEVEL
+  /* Compensate for permanent refcount bump at the bucket level */
+  if (new_log2_pages > 0)
+#endif
+    tmp_b.refcnt = h->saved_bucket.refcnt + 1;
   ASSERT (tmp_b.refcnt > 0);
   tmp_b.lock = 0;
   CLIB_MEMORY_BARRIER ();
   b->as_u64 = tmp_b.as_u64;
-  /* free the old bucket */
-  v = BV (clib_bihash_get_value) (h, h->saved_bucket.offset);
-  BV (value_free) (h, v, h->saved_bucket.log2_pages);
+
+#if BIHASH_KVP_AT_BUCKET_LEVEL
+  if (h->saved_bucket.log2_pages > 0)
+    {
+#endif
+
+      /* free the old bucket, except at the bucket level if so configured */
+      v = BV (clib_bihash_get_value) (h, h->saved_bucket.offset);
+      BV (value_free) (h, v, h->saved_bucket.log2_pages);
+
+#if BIHASH_KVP_AT_BUCKET_LEVEL
+    }
+#endif
+
+
   BV (clib_bihash_alloc_unlock) (h);
   return (0);
 }
@@ -762,8 +863,10 @@
 
   ASSERT (valuep);
 
+#if BIHASH_LAZY_INSTANTIATE
   if (PREDICT_FALSE (alloc_arena (h) == 0))
     return -1;
+#endif
 
   hash = BV (clib_bihash_hash) (search_key);
 
@@ -812,12 +915,14 @@
 
   s = format (s, "Hash table %s\n", h->name ? h->name : (u8 *) "(unnamed)");
 
+#if BIHASH_LAZY_INSTANTIATE
   if (PREDICT_FALSE (alloc_arena (h) == 0))
     return format (s, "[empty, uninitialized]");
+#endif
 
   for (i = 0; i < h->nbuckets; i++)
     {
-      b = &h->buckets[i];
+      b = BV (clib_bihash_get_bucket) (h, i);
       if (BV (clib_bihash_bucket_is_empty) (b))
 	{
 	  if (verbose > 1)
@@ -832,8 +937,9 @@
 
       if (verbose)
 	{
-	  s = format (s, "[%d]: heap offset %lld, len %d, linear %d\n", i,
-		      b->offset, (1 << b->log2_pages), b->linear_search);
+	  s = format
+	    (s, "[%d]: heap offset %lld, len %d, refcnt %d, linear %d\n", i,
+	     b->offset, (1 << b->log2_pages), b->refcnt, b->linear_search);
 	}
 
       v = BV (clib_bihash_get_value) (h, b->offset);
@@ -909,12 +1015,15 @@
   BVT (clib_bihash_bucket) * b;
   BVT (clib_bihash_value) * v;
 
+
+#if BIHASH_LAZY_INSTANTIATE
   if (PREDICT_FALSE (alloc_arena (h) == 0))
     return;
+#endif
 
   for (i = 0; i < h->nbuckets; i++)
     {
-      b = &h->buckets[i];
+      b = BV (clib_bihash_get_bucket) (h, i);
       if (BV (clib_bihash_bucket_is_empty) (b))
 	continue;
 
diff --git a/src/vppinfra/bihash_template.h b/src/vppinfra/bihash_template.h
index 6abe7a3..13a348f 100644
--- a/src/vppinfra/bihash_template.h
+++ b/src/vppinfra/bihash_template.h
@@ -44,6 +44,11 @@
 #define BIHASH_FREELIST_LENGTH 17
 #endif
 
+/* default is 2MB, use 30 for 1GB */
+#ifndef BIHASH_LOG2_HUGEPAGE_SIZE
+#define BIHASH_LOG2_HUGEPAGE_SIZE 21
+#endif
+
 #define _bv(a,b) a##b
 #define __bv(a,b) _bv(a,b)
 #define BV(a) __bv(a,BIHASH_TYPE)
@@ -103,6 +108,7 @@
    */
   u64 alloc_arena_next;	/* Next offset from alloc_arena to allocate, definitely NOT a constant */
   u64 alloc_arena_size;	/* Size of the arena */
+  u64 alloc_arena_mapped;	/* Size of the mapped memory in the arena */
   /* Two SVM pointers stored as 8-byte integers */
   u64 alloc_lock_as_u64;
   u64 buckets_as_u64;
@@ -111,7 +117,7 @@
   u32 nbuckets;	/* Number of buckets */
   /* Set when header valid */
   volatile u32 ready;
-  u64 pad[2];
+  u64 pad[1];
 }) BVT (clib_bihash_shared_header);
 /* *INDENT-ON* */
 
@@ -175,19 +181,23 @@
 #if BIHASH_32_64_SVM
 #undef alloc_arena_next
 #undef alloc_arena_size
+#undef alloc_arena_mapped
 #undef alloc_arena
 #undef CLIB_BIHASH_READY_MAGIC
 #define alloc_arena_next(h) (((h)->sh)->alloc_arena_next)
 #define alloc_arena_size(h) (((h)->sh)->alloc_arena_size)
+#define alloc_arena_mapped(h) (((h)->sh)->alloc_arena_mapped)
 #define alloc_arena(h) ((h)->alloc_arena)
 #define CLIB_BIHASH_READY_MAGIC 0xFEEDFACE
 #else
 #undef alloc_arena_next
 #undef alloc_arena_size
+#undef alloc_arena_mapped
 #undef alloc_arena
 #undef CLIB_BIHASH_READY_MAGIC
 #define alloc_arena_next(h) ((h)->sh.alloc_arena_next)
 #define alloc_arena_size(h) ((h)->sh.alloc_arena_size)
+#define alloc_arena_mapped(h) ((h)->sh.alloc_arena_mapped)
 #define alloc_arena(h) ((h)->alloc_arena)
 #define CLIB_BIHASH_READY_MAGIC 0
 #endif
@@ -285,7 +295,10 @@
   (BVT (clib_bihash_bucket) * b)
 {
   /* Note: applied to locked buckets, test offset */
-  return b->offset == 0;
+  if (BIHASH_KVP_AT_BUCKET_LEVEL == 0)
+    return b->offset == 0;
+  else
+    return (b->log2_pages == 0 && b->refcnt == 1);
 }
 
 static inline uword BV (clib_bihash_get_offset) (BVT (clib_bihash) * h,
@@ -345,19 +358,34 @@
 format_function_t BV (format_bihash_kvp);
 format_function_t BV (format_bihash_lru);
 
+static inline
+BVT (clib_bihash_bucket) *
+BV (clib_bihash_get_bucket) (BVT (clib_bihash) * h, u64 hash)
+{
+#if BIHASH_KVP_AT_BUCKET_LEVEL
+  uword offset;
+  offset = (hash & (h->nbuckets - 1));
+  offset = offset * (sizeof (BVT (clib_bihash_bucket))
+		     + (BIHASH_KVP_PER_PAGE * sizeof (BVT (clib_bihash_kv))));
+  return ((BVT (clib_bihash_bucket) *) (((u8 *) h->buckets) + offset));
+#endif
+
+  return h->buckets + (hash & (h->nbuckets - 1));
+}
+
 static inline int BV (clib_bihash_search_inline_with_hash)
   (BVT (clib_bihash) * h, u64 hash, BVT (clib_bihash_kv) * key_result)
 {
-  u32 bucket_index;
   BVT (clib_bihash_value) * v;
   BVT (clib_bihash_bucket) * b;
   int i, limit;
 
+#if BIHASH_LAZY_INSTANTIATE
   if (PREDICT_FALSE (alloc_arena (h) == 0))
     return -1;
+#endif
 
-  bucket_index = hash & (h->nbuckets - 1);
-  b = &h->buckets[bucket_index];
+  b = BV (clib_bihash_get_bucket) (h, hash);
 
   if (PREDICT_FALSE (BV (clib_bihash_bucket_is_empty) (b)))
     return -1;
@@ -400,17 +428,12 @@
   return BV (clib_bihash_search_inline_with_hash) (h, hash, key_result);
 }
 
-static inline
-BVT (clib_bihash_bucket) *
-BV (clib_bihash_get_bucket) (BVT (clib_bihash) * h, u64 hash)
-{
-  return h->buckets + (hash & (h->nbuckets - 1));
-}
-
 static inline void BV (clib_bihash_prefetch_bucket)
   (BVT (clib_bihash) * h, u64 hash)
 {
-  clib_prefetch_load (BV (clib_bihash_get_bucket) (h, hash));
+  CLIB_PREFETCH (BV (clib_bihash_get_bucket) (h, hash),
+		 BIHASH_BUCKET_PREFETCH_CACHE_LINES * CLIB_CACHE_LINE_BYTES,
+		 LOAD);
 }
 
 static inline void BV (clib_bihash_prefetch_data)
@@ -419,8 +442,10 @@
   BVT (clib_bihash_value) * v;
   BVT (clib_bihash_bucket) * b;
 
+#if BIHASH_LAZY_INSTANTIATE
   if (PREDICT_FALSE (alloc_arena (h) == 0))
     return;
+#endif
 
   b = BV (clib_bihash_get_bucket) (h, hash);
 
@@ -445,8 +470,10 @@
 
   ASSERT (valuep);
 
+#if BIHASH_LAZY_INSTANTIATE
   if (PREDICT_FALSE (alloc_arena (h) == 0))
     return -1;
+#endif
 
   b = BV (clib_bihash_get_bucket) (h, hash);
 
diff --git a/src/vppinfra/bihash_vec8_8.h b/src/vppinfra/bihash_vec8_8.h
index f50234e..15c6d8c 100644
--- a/src/vppinfra/bihash_vec8_8.h
+++ b/src/vppinfra/bihash_vec8_8.h
@@ -16,9 +16,15 @@
 #undef BIHASH_KVP_PER_PAGE
 #undef BIHASH_32_64_SVM
 #undef BIHASH_ENABLE_STATS
+#undef BIHASH_KVP_AT_BUCKET_LEVEL
+#undef BIHASH_LAZY_INSTANTIATE
+#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
 
 #define BIHASH_TYPE _vec8_8
 #define BIHASH_KVP_PER_PAGE 4
+#define BIHASH_KVP_AT_BUCKET_LEVEL 0
+#define BIHASH_LAZY_INSTANTIATE 1
+#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 1
 
 #ifndef __included_bihash_vec8_8_h__
 #define __included_bihash_vec8_8_h__
diff --git a/src/vppinfra/linux/mem.c b/src/vppinfra/linux/mem.c
index 3b7294f..d86402a 100644
--- a/src/vppinfra/linux/mem.c
+++ b/src/vppinfra/linux/mem.c
@@ -344,6 +344,38 @@
     }
 }
 
+uword
+clib_mem_vm_reserve (uword start, uword size, u32 log2_page_sz)
+{
+  uword off, pagesize = 1 << log2_page_sz;
+  int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+  u8 *p;
+
+  if (start)
+    mmap_flags |= MAP_FIXED;
+
+  size = round_pow2 (size, pagesize);
+
+  p = uword_to_pointer (start, void *);
+  p = mmap (p, size + pagesize, PROT_NONE, mmap_flags, -1, 0);
+
+  if (p == MAP_FAILED)
+    return ~0;
+
+  off = round_pow2 ((uword) p, pagesize) - (uword) p;
+
+  /* trim start and end of reservation to be page aligned */
+  if (off)
+    {
+      munmap (p, off);
+      p += off;
+    }
+
+  munmap (p + size, pagesize - off);
+
+  return (uword) p;
+}
+
 u64 *
 clib_mem_vm_get_paddr (void *mem, int log2_page_size, int n_pages)
 {
diff --git a/src/vppinfra/mem.h b/src/vppinfra/mem.h
index 4fedd10..f254601 100644
--- a/src/vppinfra/mem.h
+++ b/src/vppinfra/mem.h
@@ -411,6 +411,7 @@
 u64 clib_mem_get_fd_page_size (int fd);
 uword clib_mem_get_default_hugepage_size (void);
 int clib_mem_get_fd_log2_page_size (int fd);
+uword clib_mem_vm_reserve (uword start, uword size, u32 log2_page_sz);
 u64 *clib_mem_vm_get_paddr (void *mem, int log2_page_size, int n_pages);
 
 typedef struct
diff --git a/src/vppinfra/pmalloc.c b/src/vppinfra/pmalloc.c
index cca8039..e0f3f3a 100644
--- a/src/vppinfra/pmalloc.c
+++ b/src/vppinfra/pmalloc.c
@@ -63,9 +63,8 @@
 int
 clib_pmalloc_init (clib_pmalloc_main_t * pm, uword base_addr, uword size)
 {
-  uword off, pagesize;
+  uword base, pagesize;
   u64 *pt = 0;
-  int mmap_flags;
 
   ASSERT (pm->error == 0);
 
@@ -84,32 +83,16 @@
 
   pm->max_pages = size >> pm->def_log2_page_sz;
 
-  /* reserve VA space for future growth */
-  mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+  base = clib_mem_vm_reserve (base_addr, size, pm->def_log2_page_sz);
 
-  if (base_addr)
-    mmap_flags |= MAP_FIXED;
-
-  pm->base = mmap (uword_to_pointer (base_addr, void *), size + pagesize,
-		   PROT_NONE, mmap_flags, -1, 0);
-
-  if (pm->base == MAP_FAILED)
+  if (base == ~0)
     {
-      pm->error = clib_error_return_unix (0, "failed to reserve %u pages");
+      pm->error = clib_error_return (0, "failed to reserve %u pages",
+				     pm->max_pages);
       return -1;
     }
 
-  off = round_pow2 (pointer_to_uword (pm->base), pagesize) -
-    pointer_to_uword (pm->base);
-
-  /* trim start and end of reservation to be page aligned */
-  if (off)
-    {
-      munmap (pm->base, off);
-      pm->base += off;
-    }
-
-  munmap (pm->base + ((uword) pm->max_pages * pagesize), pagesize - off);
+  pm->base = uword_to_pointer (base, void *);
   return 0;
 }
 
diff --git a/src/vppinfra/test_bihash_template.c b/src/vppinfra/test_bihash_template.c
index c1a4469..86039d8 100644
--- a/src/vppinfra/test_bihash_template.c
+++ b/src/vppinfra/test_bihash_template.c
@@ -337,6 +337,16 @@
 	{
 	  for (i = 0; i < tm->nitems; i++)
 	    {
+	      /* Prefetch buckets 8 iterations ahead */
+	      if (1 && (i < (tm->nitems - 8)))
+		{
+		  BVT (clib_bihash_kv) pref_kv;
+		  u64 pref_hash;
+		  pref_kv.key = tm->keys[i + 8];
+		  pref_hash = BV (clib_bihash_hash) (&pref_kv);
+		  BV (clib_bihash_prefetch_bucket) (h, pref_hash);
+		}
+
 	      kv.key = tm->keys[i];
 	      if (BV (clib_bihash_search) (h, &kv, &kv) < 0)
 		if (BV (clib_bihash_search) (h, &kv, &kv) < 0)
@@ -356,8 +366,10 @@
 	  total_searches = (uword) tm->search_iter * (uword) tm->nitems;
 
 	  if (delta > 0)
-	    fformat (stdout, "%.f searches per second\n",
-		     ((f64) total_searches) / delta);
+	    fformat (stdout,
+		     "%.f searches per second, %.2f nsec per search\n",
+		     ((f64) total_searches) / delta,
+		     1e9 * (delta / ((f64) total_searches)));
 
 	  fformat (stdout, "%lld searches in %.6f seconds\n", total_searches,
 		   delta);
@@ -409,6 +421,16 @@
 	    {
 	      for (j = 0; j < tm->nitems; j++)
 		{
+		  /* Prefetch buckets 8 iterations ahead */
+		  if (1 && (j < (tm->nitems - 8)))
+		    {
+		      BVT (clib_bihash_kv) pref_kv;
+		      u64 pref_hash;
+		      pref_kv.key = tm->keys[j + 8];
+		      pref_hash = BV (clib_bihash_hash) (&pref_kv);
+		      BV (clib_bihash_prefetch_bucket) (h, pref_hash);
+		    }
+
 		  kv.key = tm->keys[j];
 		  rv = BV (clib_bihash_search) (h, &kv, &kv);
 		  if (j <= i && rv >= 0)